[moved your post to the bottom for consistency with this thread] On Mar 23, 2011, at 1:41 PM, Srushti wrote:
> On 22 March 2011 17:22, <rspec-users-requ...@rubyforge.org> wrote: > > Date: Tue, 22 Mar 2011 06:52:02 -0500 > From: David Chelimsky <dchelim...@gmail.com> > To: rspec-users <rspec-users@rubyforge.org> > Subject: Re: [rspec-users] Post call verification > Message-ID: <3237c672-2bb7-446c-9ae5-e25447ce2...@gmail.com> > Content-Type: text/plain; charset="us-ascii" > > 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. > > If anyone is interested, I wrote a quick little gem to add spies to > rspec-mocks. Basically, it adds a method called spy which internally returns > a mock.as_null_object, and a matcher for "have_received(:method)" & > "have_received(:method).with(args)". > David, my understanding of your point (or at least, part of it) is that you'd > rather use mocks since they fail on the method calls that have never been set > up. That's not what I said. Please re-read my examples above - they are about the cases where a stubbed return value matters for the code to run, which happens much more often than it would if we all followed Tell, Don't Ask (but we don't so much). > The way I see it, if I want something to fail I'd rather make it explicit. > I'd rather explicitly state that "object.should_not > have_received(:wrong_method)" so that the test is perfectly clear, than want > to use a standard mock which would fail . This is the same reason I imagine > we have "lambda {a_call}.should_not raise_error". We don't really need that > since the test fails because of an exception nonetheless. Of course, mocks > would also work as a general check against calls to other methods. > The last time I used something like this (when using RhinoMocks working on a > .net project) if you had to have a return (that your production code would > use) you would set up the expectation before hand with a :should_receive. You > would only use the :have_received matcher on calls that you didn't need to > set an expectation on. Even though this might be seem to be a little > consistent, with a little of this and a little of that, it worked out quite > well on us, with the added benefit of not needing stubs on methods where the > return value doesn't matter and you saving a little bit of noise in your > tests. > Nonetheless, here's the gem: http://rubygems.org/gems/stirlitz > Thanks, > Srushti >
_______________________________________________ rspec-users mailing list rspec-users@rubyforge.org http://rubyforge.org/mailman/listinfo/rspec-users