[rspec-users] Post call verification

Matt Wynne matt at mattwynne.net
Sat Mar 19 11:53:09 EDT 2011


On 19 Mar 2011, at 13:35, David Chelimsky wrote:

> On Mar 18, 2011, at 10:37 AM, Srushti Ambekallu wrote:
> 
>> Hey all,
>> 
>> I would like to be able to be able to have mocks where I can make all the calls and assert that it was called afterwards. This would be especially useful when asserting on a doing-method whose return value is not being considered.
>> e.g.
>> service = mock(ExternalService)
>> ExternalService.stub!(:new).and_return(service)
>> user = User.new
>> user.activate
>> service.should_have_received(:publish_user_activation).with(user)
>> Now this obviously can't replace all assertions done with should_receive, but I know there are at least a few cases where this would come in handy and be more readable. I know while writing tests, I usually write the actual call (in this case the 'post') and then go up a couple of lines to write the should_receive. I think it would be more natural to verify it after the fact rather than before. I seem to remember there was another mocking library which did something quite close to this, but I just can't seem to find it just now. What does everyone think? I could try and implement this myself, but just wanted to see if there was any interest, or any one had a good reason not to include this.
> 
> This pattern is called a test spy, and there has been much discussion of it on this list:
> 
> http://groups.google.com/group/rspec/search?group=rspec&q=test+spies&qt_g=Search+this+group
> 
> The biggest issue for me is that message expectations often get set with a stub return value:
> 
>   foo.should_receive(:bar).and_return(:baz)
>   foo(:bar)
> 
> In a world of test spies, this would be:
> 
>   foo.stub(:bar).and_return(:baz)
>   foo(:bar)
>   foo.should_have_received(:bar).with(:bam)
> 
> This requires more code in the example, and creates an otherwise unnecessary binding between the stub and the expectation. Also, note that the stub doesn't constrain the argument to bar(), but should_have_received() does (in this example). If we were to do that the other way:
> 
>   foo.stub(:bar).with(:baz).and_return(:bam)
>   bar(:something_other_than_baz)
>   foo.should_have_received(:bar)
> 
> ... should this pass or fail? As rspec-mocks works today, it could only pass if we had an additional stub at the beginning.
> 
>   foo.stub(:bar)
>   foo.stub(:bar).with(:baz).and_return(:bam)
>   bar(:something_other_than_baz)
>   foo.should_have_received(:bar)
> 
> ... because calling bar(:anything_other_than_baz) would not work due to the with() constraint.
> 
> If we agree it should fail, then that's pretty confusing as well, since foo did actually receive bar() and the only way to understand to failure is to look back at the stub with the with() constraint.
> 
> I could go on but I think this makes the point. We don't have test spies in RSpec yet because a) I don't personally find them valuable and b) they introduce more problems than they solve.
> 
> That said, if anyone cares to write an external library to support this, I'd gladly work with you to make sure RSpec provides you the extension points you need.
> 
> Cheers,
> David

It's also worth pointing out that can quite easily write your own test spy for the odd occasion when it seems necessary:

class FakeExternalService
  attr_reader :who_was_published

  def publish_user_activation(user)
    @who_was_published = user
  end
end


> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users

cheers,
Matt

matt at mattwynne.net
07974 430184

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110319/564fd65e/attachment.html>


More information about the rspec-users mailing list