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 -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110318/f91f35d5/attachment.html>
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 -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110319/a492aa31/attachment-0001.html>
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, > DavidIt''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 > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-userscheers, Matt matt at mattwynne.net 07974 430184 -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110319/564fd65e/attachment.html>
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 at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-usersThis 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 Cheers, Tom -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110322/2c226b28/attachment.html>
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 at 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/matahariThanks, Tom. Let me know if there is anything you need in RSpec to make it easy to plug this in. Cheers, David -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110322/cb684711/attachment-0001.html>
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. 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 On 22 March 2011 17:22, <rspec-users-request at rubyforge.org> wrote:> > Date: Tue, 22 Mar 2011 06:52:02 -0500 > From: David Chelimsky <dchelimsky at gmail.com> > To: rspec-users <rspec-users at rubyforge.org> > Subject: Re: [rspec-users] Post call verification > Message-ID: <3237C672-2BB7-446C-9AE5-E25447CE25C8 at 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 at 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 > > ******************************************* >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110324/f69cd771/attachment-0001.html>
[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-request at rubyforge.org> wrote: > > Date: Tue, 22 Mar 2011 06:52:02 -0500 > From: David Chelimsky <dchelimsky at gmail.com> > To: rspec-users <rspec-users at rubyforge.org> > Subject: Re: [rspec-users] Post call verification > Message-ID: <3237C672-2BB7-446C-9AE5-E25447CE25C8 at 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 at 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 >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110323/5a292c51/attachment.html>
On 24/03/11 12:29 AM, David Chelimsky wrote:> [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-request at rubyforge.org >> <mailto:rspec-users-request at rubyforge.org>> wrote: >> >> >> Date: Tue, 22 Mar 2011 06:52:02 -0500 >> From: David Chelimsky <dchelimsky at gmail.com >> <mailto:dchelimsky at gmail.com>> >> To: rspec-users <rspec-users at rubyforge.org >> <mailto:rspec-users at rubyforge.org>> >> Subject: Re: [rspec-users] Post call verification >> Message-ID: <3237C672-2BB7-446C-9AE5-E25447CE25C8 at gmail.com >> <mailto:3237C672-2BB7-446C-9AE5-E25447CE25C8 at 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 >> <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 at rubyforge.org <mailto:rspec-users at 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 at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-usersI guess you''re right, and I take back that point. I haven''t used test spies in a while, so I might get back to you on this in a while (I''m going to start using it whenever I can now), but I guess it''s a matter of how often you depend on a return value. Your experience seems to be, most of the time, or at least often enough that it doesn''t make sense to add test spies. You''re probably right, but I''ll give it a go nonetheless. There have been at least a few times that I''ve wished I had access to test spies. These might have been rare occurrences and not enough to justify polluting the api with a different type of asserting on calls. On the other hand, I might have just gotten used to working with rspec-mocks over time to stop looking for those types of usages (which I used to see enough of when working in other languages & mocking frameworks). Regardless, I''ll report back in a while fighting for inclusion again, or admitting everything''s fine the way it is. Thanks, Srushti -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110324/e1610bcb/attachment-0001.html>