In writing Rspec tests, I'm often frustrated with should_receive
. I'd like to know if there's a less intrusive alternative.
For example:
describe "making a cake" do
it "should use some other methods" do
@baker.should_receive(:make_batter)
@baker.make_cake
end
end
The call to should_receive
is a good description, but it breaks my code, because should_receive
works by masking the original method, and make_cake
can't proceed unless make_batter
actually returns some batter. So I change it to this:
@baker.should_receive(:make_batter).and_return(@batter)
This is ugly because:
make_batter
correctly returns @batter
, but I'm actually forcing the fake version of make_batter
to return that.@batter
make_batter
has any important side effects (which could be a code smell, I suppose) I have to make those happen, too.I wish that should_receive(:make_batter)
would verify the method call and pass it on to the original method. If I wanted to stub its behavior for better isolation testing, I would do so explicitly: @baker.stub(:make_batter).and_return(@batter)
.
Is there a way to do something like should_receive
without preventing the original method call? Is my problem a symptom of bad design?
It looks like the nicer API to delegate to the original method that Myron Marston alluded to has actually been added in rspec-mocks v2.12.0
and_call_original
which delegates to the original method.So now you can simply do this any time you "want to set a message expecation without interfering with how the object responds to the message":
@baker.should_receive(:make_batter).and_call_original
Thanks for adding this, Myron.
You can have should_receive
run the original method like so:
@baker.should_receive(:make_batter, &@baker.method(:make_batter))
Both should_receive
and stub
support passing a block implementation (that is eval'd when the method is called). &@baker.method(:make_batter)
gets a handle to the original method object, and passes it along as the block implementation.
FWIW, we'd like to provide a nicer API to delegate to the original method (see this issue), but it's difficult to add that functionality without breaking backwards compatibility.
You're having this problem with should_receive
because you're testing implementation details of the make_cake
method. When writing tests you should only focus on behavior and not on a sequence of internal method calls. Otherwise refactoring your code later would result in a huge refactoring of all your tests as well.
Mocks and Stubs come in handy when you want to test your classes in isolation. When writing unit tests your test subject should be isolated from any other object. Both act as a stand-in when you're working with multiple objects at once. In this case you can use should_receive
to ensure that your test subject correctly delegates some task to another object by calling a method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With