Blog

Saturday, July 30, 2011

Gotcha with testing expected params with Bourne/Mocha in Rails Controller

This is something that always trips me up using Bourne (a fork of Mocha, a Ruby mocking and stubbing library) to test expected params in a Rails Controller. Take this spec:

context "with filter" do
  before do
    get :index, :search => {:brand_id => '1'}
  end

  it 'should find latest search results' do
    SearchResult.should have_received(:latest).with('brand_id' => '1')
  end
end

This test fails, but what if I tried this:

context "with filter" do
  before do
    get :index, :search => {:brand_id => '1'}
  end

  it 'should find latest search results' do
    SearchResult.should have_received(:latest).with(:brand_id => '1')
  end
end

This will fail. Note I am now checking the parameters passed using symbols for the keys. I receive the following error:


Failure/Error: SearchResult.should have_received(:latest).with(has_entry(:brand_id => '1'))
     expected exactly once, not yet invoked: SearchResult(id: integer, search_id: integer, body: text, source: text, url: string, created_at: datetime, updated_at: datetime).latest(has_entry(:brand_id => '1'))

This is because the params that are passed to the controller have keys as strings rather than symbols:

{'brand_id' => '1'}

So what's the solution? I could actually use HashWithIndifferentAccess which gives access to a Hash using strings or symbols for keys. This is in fact the class that is used for the params hash in the controller:

it 'should find latest search results' do
    SearchResult.should have_received(:latest).with(HashWithIndifferentAccess.new(:brand_id => '1'))
  end

Or, as a standard practice, when dealing with parameters passed to a controller within a spec, always use strings:

context "with filter" do
  before do
    get :index, 'search' => {'brand_id' => '1'}
  end

  it 'should find latest search results' do
    SearchResult.should have_received(:latest).with('brand_id' => '1')
  end
end

I think this is a lot simpler, cleaner. Interestingly, I didn't pass the brand_id as a Fixnum:

before do
    get :index, 'search' => {'brand_id' => 1}
  end

I would have got the same error:

Failure/Error: SearchResult.should have_received(:latest).with('brand_id' => 1)
     expected exactly once, not yet invoked: SearchResult(id: integer, search_id: integer, body: text, source: text, url: string, created_at: datetime, updated_at: datetime).latest('brand_id' => 1)

As the brand_id is present in the hash as a String, not a Fixnum.

Please note this blog is no longer maintained. Please visit CivilCode Inc - Custom Software Development.