Complex Hash Expectations in RSpec
When spec-ing something that calls method which takes a set of nested hashes (as many Rails methods do), it may be tempting to use “#hash_including”:http://blog.davidchelimsky.net/2008/5/27/rspec-1-1-4 to test for only the values you care about. However @#hash_including@ won’t work the way we might hope for nested hashes. Take the following (highly contrived) example:
describe CoffeeMaker do
before :each do
@it = CoffeeMaker.new
end
it "should receive #make_coffee with roast => medium" do
@it.should_receive(:make_coffee).
with(:water => :filtered,
:beans => hash_including(:roast => :medium))
@it.make_coffee(:water => :filtered,
:beans => {
rigin => "Guatemala",
:roast => :medium
})
end
end
If we run this we get a failure:
1)
Spec::Mocks::MockExpectationError in 'CoffeeMaker should receive #make_coffee with roast => medium'
Mock 'CoffeeMaker' expected :make_coffee with ({:water=>:filtered, :beans=>#
rigin=>"Guatemala"}})
Clearly @#hash_including@ was only intended to work with shallow hashes.
Instead, we can use a lesser-known feature of RSpec’s mock objects to test only the values we care about:
describe CoffeeMaker do
before :each do
@it = CoffeeMaker.new
end
it "should receive #make_coffee with roast => medium" do
@it.should_receive(:make_coffee) do |options|
options[:beans][:roast].should == :medium
end
@it.make_coffee(:water => :filtered,
:beans => {
rigin => "Guatemala",
:roast => :medium
})
end
end
Here we’ve supplied a block to @#should_receive@. The block will be called when the mocked method is called, and will be passed whatever arguments the mocked method was called with. Inside we can use any kind of RSpec assertions we like.
Here’s the failure message if we supply @:roast => :dark@ instead of @:medium@:
Spec::Mocks::MockExpectationError in 'CoffeeMaker should receive #make_coffee with roast => medium'
Mock 'CoffeeMaker' received :make_coffee but passed block failed with: expected: :medium,
got: :dark (using ==)

This work, unless otherwise expressly stated, is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.
Related posts:
