Rspec'ing Rails Controllers

Posted on February 26, 2007

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
Comments
  1. Dan KubbFebruary 26, 2007 @ 06:29 AM

    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.

  2. DougMarch 22, 2007 @ 12:24 AM

    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.

  3. Graeme NelsonMarch 22, 2007 @ 08:05 PM

    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 :)

  4. DougMarch 23, 2007 @ 07:12 PM

    You’re right, those cases are probably prime candidates for refactoring…good point.