An RSpec2 / Shoulda Controller Gotcha

September 8, 2010

I'm in the process of upgrading a Rails 2.3.8 app to Rails 3, and part of that process involved upgrading to RSpec2. I made use of Shoulda, mostly in my controller tests, and I ran into a frustrating issue that hopefully I can prevent for other people with this post.

A simple example controller spec looked like this:

require 'spec_helper'

describe AchievementsController, "GET index" do
  before { get :index }

  it { should respond_with(:success) }
  it { should assign_to(:achievements).with_kind_of(Array) }
  it { should assign_to(:members).with_kind_of(Array) }
  it { should render_template(:index) }
end

Running this spec with RSpec2 generated this error:

Failures:
  1) AchievementsController GET index
     Failure/Error: it { should respond_with(:success) }
     undefined method `response_code' for nil:NilClass
     # gems/activesupport-3.0.0/lib/active_support/whiny_nil.rb:48:in `method_missing'
     # (path)shoulda/lib/shoulda/action_controller/matchers/respond_with_matcher.rb:57:in `response_code'
     # (path)shoulda/lib/shoulda/action_controller/matchers/respond_with_matcher.rb:48:in `correct_status_code?'
     # (path)shoulda/lib/shoulda/action_controller/matchers/respond_with_matcher.rb:30:in `matches?'
     # ./spec/controllers/achievements_controller_spec.rb:12
     # gems/activesupport-3.0.0/lib/active_support/dependencies.rb:239:in `inject'

  2) AchievementsController GET index
     Failure/Error: it { should assign_to(:achievements).with_kind_of(Array) }
     Expected action to assign a value for @achievements
     # ./spec/controllers/achievements_controller_spec.rb:13
     # gems/ree-1.8.7-2010.02@rails3/gems/activesupport-3.0.0/lib/active_support/dependencies.rb:239:in `inject'

  3) AchievementsController GET index
     Failure/Error: it { should assign_to(:members).with_kind_of(Array) }
     Expected action to assign a value for @members
     # ./spec/controllers/achievements_controller_spec.rb:14
     # gems/ree-1.8.7-2010.02@rails3/gems/activesupport-3.0.0/lib/active_support/dependencies.rb:239:in `inject'

The solution is ridiculously simple:

require 'spec_helper'

describe AchievementsController, "GET index" do
  before { get :index }
  subject { controller } # <- ADD THIS

  it { should respond_with(:success) }
  it { should assign_to(:achievements).with_kind_of(Array) }
  it { should assign_to(:members).with_kind_of(Array) }
  it { should render_template(:index) }
end

It makes perfect sense in retrospect, but it's a little frustrating that in order to find this solution, I had to look through Shoulda's commit log for a mention of rspec2, which noted a change to its Cucumber features, which included this subject line.