Post call verification

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.

Thanks,
Srshti

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

On 19 Mar 2011, at 13:35, David C. wrote:

service.should_have_received(:publish_user_activation).with(user)

foo.should_have_received(:bar)
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
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users

cheers,
Matt

[email protected]
07974 430184

On Mar 22, 2011, at 4:13 AM, Tom S. wrote:

user = User.new
foo.should_receive(:bar).and_return(:baz)
foo.stub(:bar).with(:baz).and_return(:bam)
… because calling bar(:anything_other_than_baz) would not work due to the
with() constraint.
rspec-users mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users

This is a long-running discussion and I suspect it comes down to personal
preference in the end more than anything else. However, I have done some work to
get a basic test spy library working with rspec which tries to avoid unnecessary
stubbing to allow assertion on method calls (i.e. you only need to set up a stub
as well when you need to manipulate the return value). It’s in its infant stages
and needs some TLC (in particular, its factory method ‘spy’ is in the global
namespace, when it could and should be dealt with more elegantly), but it may be
of some use for test spy fanatics… GitHub - rentalcustard/matahari: Test spy library for Ruby

Thanks, Tom. Let me know if there is anything you need in RSpec to make
it easy to plug this in.

Cheers,
David

On 19 Mar 2011, at 13:35, David C. wrote:

service.should_have_received(:publish_user_activation).with(user)

foo.should_have_received(:bar)
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.
http://rubyforge.org/mailman/listinfo/rspec-users
This is a long-running discussion and I suspect it comes down to
personal preference in the end more than anything else. However, I have
done some work to get a basic test spy library working with rspec which
tries to avoid unnecessary stubbing to allow assertion on method calls
(i.e. you only need to set up a stub as well when you need to manipulate
the return value). It’s in its infant stages and needs some TLC (in
particular, its factory method ‘spy’ is in the global namespace, when it
could and should be dealt with more elegantly), but it may be of some
use for test spy fanatics… GitHub - rentalcustard/matahari: Test spy library for Ruby

Cheers,

Tom