[rspec-users] Test doubles: expect "x" and don't care about anything else

Ben Mabey ben at benmabey.com
Mon Jun 29 10:26:10 EDT 2009

Matt Wynne wrote:
> On 28 Jun 2009, at 23:02, Wincent Colaiuta wrote:
>> El 28/6/2009, a las 23:04, Matt Wynne escribió:
>>> On 28 Jun 2009, at 13:07, Wincent Colaiuta wrote:
>>>> I've had one of my recurring doubts about test doubles come up again.
>>>> The full post is here but I'll abbreviate the content in this 
>>>> message in any case:
>>>> https://wincent.com/blog/thinking-about-switching-to-rr
>>>> Basically, in one of my controller specs I wanted to verify that 
>>>> the following line was being called and doing the right thing:
>>>> @comment = Comment.find params[:id]
>>>> I had a mock for this set up, but it broke when unrelated code in 
>>>> the model was modified (a complex callback which itself called 
>>>> Comment.find).
>>> I'd like to know more about how this happened. How did the model 
>>> object's behaviour leak into the controller spec?
>> This was a spec for the controller's "update" action, which does a 
>> "save" on the record. At one point a change was made to the model to 
>> do some complex updates in the after_save callback, and these 
>> involved doing another Comment.find call, but with different parameters.
> If I understand this correctly, there was only one call from 
> Controller -> Comment that you wanted to test; the other one was a 
> call from Comment -> Comment that happened as a side-effect.
> So I'm wondering: if you'd returned a fake (mock, stub, whatever) 
> comment from your stubbed Comment.find, would that have solved the 
> problem?
>>>> In my ideal test-double framework, I'd like to really assert two 
>>>> things about the line of code in question:
>>>> 1. That Comment.find gets called with a specific param at some 
>>>> point in time.
>>>> 2. That the @comment instance variable gets the expected value 
>>>> assigned to it.
>>> So why not use
>>> Comment.stub!(:find).with(123).and_return(mock(Comment))
>> Because there are actually two "find" calls here:
>> - the one I actually care about
>> - the other one in the after_save callback which is irrelevant to the 
>> controller
>> I original used "should_receive", not "stub", so RSpec complained 
>> about getting "find" with the unexpected parameters. If I change to 
>> "stub" then I'm losing my assertion (no longer checking that the 
>> message gets sent), injecting a different return value (adding 
>> complexity), for no visible benefit (may as well just throw away the 
>> expectation).
> What I often do is put a stub in first, which will work in all the 
> examples, then put a should_receive in one of the examples if (as 
> seems to be the case here) it's important to me to test the 
> collaboration between the objects. So it would look like this:
> describe "#update" do
>   before(:each)
>     @comment = mock(Comment)
>     Comment.stub!(:find).and_return(@comment)
>   end
>   it "should call the model to try and find the comment"
>     Comment.should_receive(:find).with(123).and_return(@comment)
>     do_request
>   end

You probably know this, but for the benefit of others... Pat made a 
change a while back that makes it so the stubbed return value will still 
be returned even if an expectation is added.  Meaning, assuming the stub 
is in the before block, you can change the expectation to:
    Comment.should_receive(:find).with(123)  # this will still return 


More information about the rspec-users mailing list