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.