I’ve been using rspec for a few months now, and I really like it. I recommend trying it out if you haven’t.
I figure it might be nice to share how I organize controller specs, and get some feedback on how others might organize their specs. I like having one test per specification like so:
specify "should redirect to new session url" do response.should_redirect_to new_session_url end
I also like calling the controller action in the setup method.
setup do ... any necessary setup info post :update, :id => model, :model => params end
Here’s what a context might look like in all its glory.
context "Create with a valid product and authenticated user" do
include ProductsControllerSpecHelper
controller_name :products
setup do
@product = mock(:product, :null_object => true)
Product.stub!(:new).and_return(@product)
@product.should_receive(:save!).and_return(true)
@product.should_receive(:categories).and_return([])
authenticate_user_mock
post :create, :product => valid_product_attributes
end
specify "should redirect to edit" do
response.should_redirect_to edit_product_url(@product)
end
specify "should assign product" do
assigns[:product].should_not_be_nil
end
end
I’ve experimented with a few different styles, from the monolithic Test::Unit-style specifications, to the one test per spec, and a few in between. I’ve settled on the same approach as you: one test per spec.
In fact I take it a bit further: one line of code per specify block.
I’ve found that approach forces me to think about testing only one single thing. I can always split things up into more than one specify block, or more than one context. I know that at some point I may have to be pragmatic and do do a two or three-liner, but I haven’t been forced to yet, and I hope I never do.
I use the setup block to set up the state of the object, and I NEVER change the state of the object outside of the setup block. This forces me to test onto a single context at one time. If I need to change the state (the return values of any methods), I set up a new context for it and test it that separately.
I’m curious to see a more involved example of this – it seems like you would end up needing to specify all your expectations in the set up and have a lot of setups that are very similar to spec the interaction of the controller with your models. I am still relatively new to rspec so this is something I’ve been wrestling with as well. Thanks for the interesting post.
Thanks for the comment Doug.
I agree the setup could become messy if you have a lot of business logic within the controller. However, If I find that I am writing a bunch of mocks and stubs for one test, then I know that I should move some of the logic down to the model layer.
I am still trying to figure out some best practices myself. I will continue to post my findings :)
You’re right, those cases are probably prime candidates for refactoring…good point.