Message Expectations on methods that receive multiple messages with RSpec
Suppose you have the following scenario:
class Foo def bar [:foo,:bar,:zoo].each { |var| instance_variable_set("@#{var.to_s}", rand(5)) } end end |
And you want to specify that Foo should receive instance_variable_set with :foo, :bar and :zoo. You can try this way:
describe Foo describe "#bar" subject { Foo.new } [...] it "sets the instance variable @zoo with a random number" do subject.should_receive(:instance_variable_set).with(:zoo) subject.bar end end end |
But this way when #bar calls instance_variable_set with :foo or :bar, you get a Spec::Mocks::MockExpectationError: “#<Foo:0x7f770e216d08> received :instance_variable_set with unexpected arguments”. Great, ain’t it? How are we supposed to fix that?
Here’s something that should work in this case:
describe Foo describe "#bar" subject { Foo.new } [...] it "sets the instance variable @zoo with a random number" do subject.stub(:instance_variable_set).as_null_object subject.should_receive(:instance_variable_set).with(:zoo) subject.bar end end end |
Since you don’t care, in this example, what instance_variable_set does, you can stub it as a null_object, which won’t complain when it gets unexpected messages. So instead of the #bar method actually calling instance_variable_set, it calls the stub!
Do you still want to assert that instance_variable_set is doing its job on setting variables? Then it’s a behavior that should be specified on another example! An example would be the following:
describe Foo describe "#bar" subject { Foo.new } [...] it "sets the instance variable @zoo with a random number" do subject.stub(:rand => (fake_random = double('fake-random'))) subject.bar subject.instance_variable_get("@zoo").should == fake_random end end end |
This approach is fairly simple and works as it should, letting you specify your code behavior without bothering with the need to assert that “ruby is doing its job right”.
What about you? Do you have any magic you would like to share about setting message expectations? Feel invited to comment!
