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

Reply via email to