[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

Reply via email to