I like to test almost same way. I don't even use stub at that place. I find, that using params is better. For example I don't need to test if params get to the model. If not, then there can't be save and test fails.
@Inimene, the reason I prefer to stub out the validation is to remove a dependency on the model. I don't want the controller test to break when I change the model validation.
@Matt, thanks for the link. That seems like a good solution in the model tests where I need some initial data to test upon. However, in the controller layer I'm sidestepping the problems with fixtures.
What I'm doing is making the tests independent of the data inside the fixtures. The only reason I'm loading them is to get loops and such in the view to execute so it finds any errors in the code. The controller tests don't care what it contains.
As mentioned in the screencast, the only thing I depend upon in the fixtures is that:
1. there's a record with the ID of 1 so I can reference it if need be
2. the data inside the fixtures is valid (so it doesn't raise any nil object errors)
If I need to test anything that depends on the content of the fixtures I'll move this to a model test and use something like what you linked to instead of fixtures.
What about to do when you're are using an authentication tool in your program, how to you tell your tests/specs that the should log in (authenticated)first?
commenting the line with the filter when running the tests in the application controller will not be the best solution, so ...? any good ideas?
Hey I've been using this technique for rspec to check authentication, it uses fixtures so I'm not sure if it is best practice but its pretty easy to set up.
http://pastie.caboo.se/98834
Please let me know if there is a better or easier way.
@Joshua, looks like a good solution. I've done something similar in the past. It is relying on the fixture data, but as long as it's not relying on the relationship of other fixture records then I think it's perfectly acceptable.
off topic : wish for screencast topic.
maybe you could show in one of the following screencasts:
helper_method :whatever
versus
helper :whatever include WhateverHelper
... this was quite confusing for me in the beginning and i still have always to think instead of doing the appropriate one automatically
@Frank, I've had issues with stubbing class methods. Sometimes the stub didn't get reset between tests which caused very difficult to find problems. So far I haven't had any problems with mocha and it feels much more solid to me.
In this episode you mentioned that it is a best practice to only do one assert per test. Why is that? I find that when testing a method I have several things I want to check to make sure they all work together.
Ryan, Thanks for the great screencasts they have helped me out tremendously. I really like your simplified tests. I'm new to the testing game (its never too late right?!) Any chance of seeing how you'd handle testing the other restful actions?
@Sean, for fully RESTful controllers I usually start with very minimal tests. It's actually more minimal than what I'm showing here. Here's an example:
http://pastie.caboo.se/159422
For the most part I'm just testing the response. Even though this willl likely result in 100% test coverage with rcov, it's not really. For example I'm not actually testing the update of the attributes in the update action. I feel it's okay to assume this kind of behavior for a standard RESTful controller. This allows me to focus more on the exceptions to the normal REST than the expected behavior.
For example, if you have some kind of authorization then it's fairly easy to add some tests for this. But if the starting specs are too complex then this exceptional behavior can easily become lost in the specs.
@Damian, the colors are from rspec. But if you want them with test/unit then I think there's a gem called redgreen which can do this.
In general I try to mirror all simple rails validations (presence, length, uniqueness etc...) with indexes and not-null fields in the database. In your screencast you stub out valid? to bypass validations, but if there were any not-null fields in your table that spec would fail.
If you added a not-null constraint to your database at a later date, you'd have to go to that spec and update the post methods to include params that correspond with the not-null fields, which can quickly become unmaintainable since your controller specs are now tied to your model's fields.
I think in your example stubbing save would have been the better way to go. I typically mock the entire class, but it's a lot of work when you use integrate_views. Stubbing save would also make the should_be new_record call unnecessary.
Thanks for the great screencasts - I've learned a lot from you.f
(paraphrasing the Prolegomena-Kant's more 'accessible' work-here) Space and time are the necessary pre-conditions of all future metaphysics.
God, I love being pretentious! (AND pedantic!)
Right, gross simplifications in response to obtuse philosophical musings over, just wanted to offer a belated thanks to you Ryan--this 'cast has just redeemed Rspec for me.
Q1 - Overall Ryan/guys would you recommend RSpec for controller & model testing over Rails tests? (just want to jump in with one of them)
Q2 - Can I assume that the core testing framework (e.g. rails unit testing versus RSpec) is a kind of separate issue to whether you using mocking or not? i.e. you could use Mocha with either the rails unit test framework or Rspec? Or is there something in Rspec that integrates moreso with mocking?
More testing (rspec) screencasts please. I get this guilty feeling even mentioning the word.
The community encourages testing, but then just refers me to a peepcode(which are great) or some chapter in a book and carries on.
If testing is so important then why are so few screencasts here on testing? We need someone like yourself to 'glamourize' testing. If every second screencast were about testing I'd feel compelled to think about testing in my everyday coding.
Ryan,
you said you had problems stubbing class methods. I have similar issues. Other tests fail because stubbing seems to "carry over".
If I run a single rspec, it passes, but rake rspec fails, which makes me think something isn't being reset.
Do you have any suggestion what to do instead?
For example, in my controller, I do
Product.find params[:id]
it's tempting to do the following in a rspec
Product.stub!(:find).and_return(some_product)
While I think the way you have structured this test is very interesting in that it covers many possible implementations for saving an object to the DB using ActiveRecord; I think there is a pitfall to this method in that you are relying/depending on the underlying platform's implementation, which you as a developer have no control over.
While there may be a small chance of the Rails implementation changing any time soon, you still have no control over this (unless you are one of the Rails developers of course :) ).
Also, I feel that writing tests in the way that has been demonstrated here, while very clever, is simply more difficult, both to write and to read. It seems like you could easily spend more time trying to figure out clever ways to write tests with very high coverage "areas", which would take away from the task of getting your app tested and implemented the specific way you want.
Also, as for readability, this test requires a deeper knowledge of the Rails framework to understand. When someone new comes in to look at the test and underlying implementation, they will see the use of the "valid?" function in the test, but yet will not find this method called anywhere in the implementation. If this person is not already familiar with how ActiveRecord is implemented, then they may have to start going through Rails docs just to make sense of the test code.
While this method for high test coverage is certainly clever, and I thought it was a great screencast, I also feel it has some serious drawbacks.
Hi there,
Love the podcasts...
Way back a year ago (items 10 and 11 above) you mention stubbing out the current_user.
Could you please elaborate on that.
What do I do if I have an rspec generated spec (from the scaffold generator) for a controller, which works fine (all the tests pass) but now I want to make the entire controller require a login. (using a before filter)
[Note: I'm using restful authentication, as provided in bort. See http://github.com/fudgestudios/bort/tree/master]
Do I need to re-write all of the tests that pass (but assume no login is required) or is there an easy way to make the whole set of tests act as though a user is logged in.
Then, I assume I just need to write a test or two which verifies correct behavior when a user is NOT logged in. Which probably just confirms a redirect.
Hi Ryan,
It would be too nice of you if you could include an episode for setting up RSpec for writing rspec tests. I tried to follow your video but was not able to set up rspec in the first place.. Help!
I'm new to Rails and trying to write functional tests, a simple test I thought. I have regular users and read-only users, and I wanted to verify that links to add/edit/delete are only displayed for the regular users. Thought I could just do an assert_select to see that an 'a' tag exists with id like 'addLink'. But for some reason the test always fails because it isn't finding the 'a' tag. I cannot find any good references on doing this. Can I just assert_select 'a', 'addLink', or must I somehow traverse through other parts of the view's form? Would appreciate any help, even if it's just pointing me to a location that provides extensive examples of testing controllers and the views they render.
Great screencast! But stubbing :valid? doesn't help when the underlying database table has the :null => false constraint on any of the columns. The database will complain when the controller tries to save the record. Any suggestions?
Great Episode Ryan, glad to get my Railscasts fix :)
Very nice screencast!
I like to test almost same way. I don't even use stub at that place. I find, that using params is better. For example I don't need to test if params get to the model. If not, then there can't be save and test fails.
@Inimene, the reason I prefer to stub out the validation is to remove a dependency on the model. I don't want the controller test to break when I change the model validation.
Nice one Ryan :)
One thing I found surprising is that you don't use Growl notifications for your autotesting. I certainly cannot live without my red and green smileys.
Instead of
post 'create'
assigns[:menu_item].should be_new_record
you can also do:
lambda { post 'create' }.should change { MenuItem.count }.by(1)
and similarly
lambda { post 'create' }.should_not change { MenuItem.count }
@pimpmaster, I normally use growl, but I just don't for the screencast because it is distracting.
Hey Ryan;
Check out Lachie Cox's Hornsby Scenarios.
http://blog.allen.com.au/2007/9/17/33-702635-151-099434-hornsby
and
http://blog.smartbomb.com.au/2007/8/29/hornsby
It's awesome for getting rid of fixtures.
@Matt, thanks for the link. That seems like a good solution in the model tests where I need some initial data to test upon. However, in the controller layer I'm sidestepping the problems with fixtures.
What I'm doing is making the tests independent of the data inside the fixtures. The only reason I'm loading them is to get loops and such in the view to execute so it finds any errors in the code. The controller tests don't care what it contains.
As mentioned in the screencast, the only thing I depend upon in the fixtures is that:
1. there's a record with the ID of 1 so I can reference it if need be
2. the data inside the fixtures is valid (so it doesn't raise any nil object errors)
If I need to test anything that depends on the content of the fixtures I'll move this to a model test and use something like what you linked to instead of fixtures.
What about to do when you're are using an authentication tool in your program, how to you tell your tests/specs that the should log in (authenticated)first?
commenting the line with the filter when running the tests in the application controller will not be the best solution, so ...? any good ideas?
thx in advance.
@QuBiT, usually I just stub out the "current_user" method in the controller to return a user of my choice. That way I can "login" any user I want.
Hey I've been using this technique for rspec to check authentication, it uses fixtures so I'm not sure if it is best practice but its pretty easy to set up.
http://pastie.caboo.se/98834
Please let me know if there is a better or easier way.
I've wondered how other people deal with this.
Cheers,
Josh
Edit: moved code into pastie --Ryan
@Joshua, looks like a good solution. I've done something similar in the past. It is relying on the fixture data, but as long as it's not relying on the relationship of other fixture records then I think it's perfectly acceptable.
off topic : wish for screencast topic.
maybe you could show in one of the following screencasts:
helper_method :whatever
versus
helper :whatever include WhateverHelper
... this was quite confusing for me in the beginning and i still have always to think instead of doing the appropriate one automatically
Why do you prefer mocha? What problems do you have with stubbing and mocking in Rspec itself.
@Frank, I've had issues with stubbing class methods. Sometimes the stub didn't get reset between tests which caused very difficult to find problems. So far I haven't had any problems with mocha and it feels much more solid to me.
In this episode you mentioned that it is a best practice to only do one assert per test. Why is that? I find that when testing a method I have several things I want to check to make sure they all work together.
@Mark, I picked up the practice after reading a Jay Fields article. He explains it better than I can:
http://blog.jayfields.com/2007/06/testing-one-assertion-per-test.html
Ryan, Thanks for the great screencasts they have helped me out tremendously. I really like your simplified tests. I'm new to the testing game (its never too late right?!) Any chance of seeing how you'd handle testing the other restful actions?
Ryan, thank you very much for your work! It is really enlightening! I actually learned from railscasts more that from awdwr book! Thank you!
By the way, I would like to ask you how you get that nice color output in autotest?
you should really put rspec in it's name, as i searched for it quite a while before actually diving in to see what it was about
@Sean, for fully RESTful controllers I usually start with very minimal tests. It's actually more minimal than what I'm showing here. Here's an example:
http://pastie.caboo.se/159422
For the most part I'm just testing the response. Even though this willl likely result in 100% test coverage with rcov, it's not really. For example I'm not actually testing the update of the attributes in the update action. I feel it's okay to assume this kind of behavior for a standard RESTful controller. This allows me to focus more on the exceptions to the normal REST than the expected behavior.
For example, if you have some kind of authorization then it's fairly easy to add some tests for this. But if the starting specs are too complex then this exceptional behavior can easily become lost in the specs.
@Damian, the colors are from rspec. But if you want them with test/unit then I think there's a gem called redgreen which can do this.
@oin, fixed. Thanks for the suggestion.
In general I try to mirror all simple rails validations (presence, length, uniqueness etc...) with indexes and not-null fields in the database. In your screencast you stub out valid? to bypass validations, but if there were any not-null fields in your table that spec would fail.
If you added a not-null constraint to your database at a later date, you'd have to go to that spec and update the post methods to include params that correspond with the not-null fields, which can quickly become unmaintainable since your controller specs are now tied to your model's fields.
I think in your example stubbing save would have been the better way to go. I typically mock the entire class, but it's a lot of work when you use integrate_views. Stubbing save would also make the should_be new_record call unnecessary.
Thanks for the great screencasts - I've learned a lot from you.f
how would you write rspec code if objects are defined from their associations like
@task = @projects.tasks.build( params[ :task ]
@tasks = @project.tasks
etc
Why do you prefer mocha over rspec's mocking scheme?
or somewhat more directly said, kino:
(paraphrasing the Prolegomena-Kant's more 'accessible' work-here) Space and time are the necessary pre-conditions of all future metaphysics.
God, I love being pretentious! (AND pedantic!)
Right, gross simplifications in response to obtuse philosophical musings over, just wanted to offer a belated thanks to you Ryan--this 'cast has just redeemed Rspec for me.
Q1 - Overall Ryan/guys would you recommend RSpec for controller & model testing over Rails tests? (just want to jump in with one of them)
Q2 - Can I assume that the core testing framework (e.g. rails unit testing versus RSpec) is a kind of separate issue to whether you using mocking or not? i.e. you could use Mocha with either the rails unit test framework or Rspec? Or is there something in Rspec that integrates moreso with mocking?
More testing (rspec) screencasts please. I get this guilty feeling even mentioning the word.
The community encourages testing, but then just refers me to a peepcode(which are great) or some chapter in a book and carries on.
If testing is so important then why are so few screencasts here on testing? We need someone like yourself to 'glamourize' testing. If every second screencast were about testing I'd feel compelled to think about testing in my everyday coding.
Ryan,
you said you had problems stubbing class methods. I have similar issues. Other tests fail because stubbing seems to "carry over".
If I run a single rspec, it passes, but rake rspec fails, which makes me think something isn't being reset.
Do you have any suggestion what to do instead?
For example, in my controller, I do
Product.find params[:id]
it's tempting to do the following in a rspec
Product.stub!(:find).and_return(some_product)
Thanks for the great screencast.
While I think the way you have structured this test is very interesting in that it covers many possible implementations for saving an object to the DB using ActiveRecord; I think there is a pitfall to this method in that you are relying/depending on the underlying platform's implementation, which you as a developer have no control over.
While there may be a small chance of the Rails implementation changing any time soon, you still have no control over this (unless you are one of the Rails developers of course :) ).
Also, I feel that writing tests in the way that has been demonstrated here, while very clever, is simply more difficult, both to write and to read. It seems like you could easily spend more time trying to figure out clever ways to write tests with very high coverage "areas", which would take away from the task of getting your app tested and implemented the specific way you want.
Also, as for readability, this test requires a deeper knowledge of the Rails framework to understand. When someone new comes in to look at the test and underlying implementation, they will see the use of the "valid?" function in the test, but yet will not find this method called anywhere in the implementation. If this person is not already familiar with how ActiveRecord is implemented, then they may have to start going through Rails docs just to make sense of the test code.
While this method for high test coverage is certainly clever, and I thought it was a great screencast, I also feel it has some serious drawbacks.
Hi there,
Love the podcasts...
Way back a year ago (items 10 and 11 above) you mention stubbing out the current_user.
Could you please elaborate on that.
What do I do if I have an rspec generated spec (from the scaffold generator) for a controller, which works fine (all the tests pass) but now I want to make the entire controller require a login. (using a before filter)
[Note: I'm using restful authentication, as provided in bort. See http://github.com/fudgestudios/bort/tree/master]
Do I need to re-write all of the tests that pass (but assume no login is required) or is there an easy way to make the whole set of tests act as though a user is logged in.
Then, I assume I just need to write a test or two which verifies correct behavior when a user is NOT logged in. Which probably just confirms a redirect.
Thanks,
John Schank
Hi Ryan,
It would be too nice of you if you could include an episode for setting up RSpec for writing rspec tests. I tried to follow your video but was not able to set up rspec in the first place.. Help!
I'm new to Rails and trying to write functional tests, a simple test I thought. I have regular users and read-only users, and I wanted to verify that links to add/edit/delete are only displayed for the regular users. Thought I could just do an assert_select to see that an 'a' tag exists with id like 'addLink'. But for some reason the test always fails because it isn't finding the 'a' tag. I cannot find any good references on doing this. Can I just assert_select 'a', 'addLink', or must I somehow traverse through other parts of the view's form? Would appreciate any help, even if it's just pointing me to a location that provides extensive examples of testing controllers and the views they render.
I am having trouble using stubs method. The method is being stubbed across tests and is breaking some of my tests. Any suggestions?
Great screencast! But stubbing :valid? doesn't help when the underlying database table has the :null => false constraint on any of the columns. The database will complain when the controller tries to save the record. Any suggestions?
Great cast!
I just wish there was a cast with more basic stuff about rspec, from installation, to running, possible errors, how do make fixtures work, etc...
Thank you