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@rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users

cheers,
Matt

m...@mattwynne.net
07974 430184

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

Reply via email to