On Mar 22, 2011, at 4:13 AM, Tom Stuart wrote: > 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 >> _______________________________________________ >> rspec-users mailing list >> rspec-users@rubyforge.org >> 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... > https://github.com/mortice/matahari
Thanks, Tom. Let me know if there is anything you need in RSpec to make it easy to plug this in. Cheers, David
_______________________________________________ rspec-users mailing list rspec-users@rubyforge.org http://rubyforge.org/mailman/listinfo/rspec-users