Lexer Tumblog

Lexer is a startup building a suite of innovative apps designed to measure marketing efficiency and effectiveness.

March 11, 2010 at 10:39am
Home

Expectations on method calls within a block in RSpec

At Lexer, we are using Solr (via SunSpot) to perform full text searches on our data. We have a Rails app that has a feature where a user can dynamically build a query and submit it via an AJAX call with the query itself represented as a JSON array of conditions.

The server side receives this JSON array, and passes it through module called JsonToSunspot which executes the equivalent Sunspot search.

So I’d receive a set of conditions as JSON that look something like this:

[
    {'with' => {'some_attribute' => 'foo'}},
    {'with_not' => {'some_other_attribute' => 'bar'}}
]

And translate them into a Sunspot invokation like this:

Sunspot.search(MyModel) do
  with(:some_attribute, 'foo')
  with_not(:some_other_attribute, 'bar')
end

Ideally, I’d like to unit test all of this without actually bringing up Sunspot. The problem was, I couldn’t see how to mock ‘self’ within the context of the block in order to set expectations that ‘with’ and ‘with_not’ were called with appropriate arguments.

Turns our you can do this with RSpec’s ‘and_yield’ method like so:

Sunspot.should_receive(:search).with(MyModel).and_yield do |context|
  context.should_receive(:with).with(:some_attribute, 'foo')
  context.should_receive(:with_not).with(:some_other_attribute, 'bar')
end

The key here is the ‘context’ variable. It is a mockable version of self. Pretty awesome huh?! Now when my JSON-to-Sunspot code executes, I can actually test that it invokes Sunspot correctly without having it running.

Special thanks to @robertpostill for the tip.