rupert
2007-Aug-12 10:34 UTC
[rspec-users] stubbing a method that yeilds sequential results
I''ve just found myself stuck trying to rspec something so am hoping someone more knowledgable can help. I have a Connector class which has a class method ''results'' that yields results it get from a network service based on a set of attributes that I pass to it. I am wanting to yield these results from my Intermediate class up to the next ''level'' so the basic no frills set up would be this: class Intermediate def self.results(attributes) Connector.each_result(attributes) do |result| yield result end end end I''ve worked out how to stub things for the case where the Connector. each_result method yields a result once #setup @result = mock("result") Connector.stub!(:each_result).and_yield(@result) @attributes = {} @results = [] @block = Proc.new { |r| @results << r } #action Intermediate.search_results(@attributes, &@block) # expectation @results.should == [@search_result] However, what I actually need to do is check each result that is yielded by the Connector.each_result method and compare it to the previous one. If they are sufficiently similar I need to merge them (and same again if the next result is sufficiently similar). I only want to yeild merged results and results that are not similar to their preceeding result(s) - I''d imagined he code to do this would be something along the lines of: class Intermediate def self.results(attributes) @saved_result = nil Connector.each_result(attributes) do |result| if results_match(result, @saved_result) @saved_result.merge!(result) else yield @saved_result unless @saved_result.nil? @saved_result = result end end yield @saved_result unless @saved_result.nil? end def results_match(this, last) return false if last.nil? .... end end I can''t for the life of me see how I should spec this though, as trying: Connector.stub!(:results).and_yield(@result1, @result2) is expecting the two results to be yielded at the same time and not sequentially. I can''t see how to stub a method to yield sequential results so I can spec the behavior for different scenarios of similarities between subsequent results. Is it possible to do this? Any help would be much appreciated Cheers Rupert
David Chelimsky
2007-Aug-12 13:38 UTC
[rspec-users] stubbing a method that yeilds sequential results
On 8/12/07, rupert <rupert_apsc at rupespad.com> wrote:> I''ve just found myself stuck trying to rspec something so am hoping > someone more knowledgable can help. > > I have a Connector class which has a class method ''results'' that > yields results it get from a network service based on a set of > attributes that I pass to it. I am wanting to yield these results > from my Intermediate class up to the next ''level'' so the basic no > frills set up would be this: > > class Intermediate > def self.results(attributes) > Connector.each_result(attributes) do |result| > yield result > end > end > end > > I''ve worked out how to stub things for the case where the Connector. > each_result method yields a result once > > #setup > @result = mock("result") > Connector.stub!(:each_result).and_yield(@result) > > @attributes = {} > @results = [] > @block = Proc.new { |r| @results << r } > > #action > Intermediate.search_results(@attributes, &@block) > > # expectation > @results.should == [@search_result] > > > However, what I actually need to do is check each result that is > yielded by the Connector.each_result method and compare it to the > previous one. If they are sufficiently similar I need to merge them > (and same again if the next result is sufficiently similar). I only > want to yeild merged results and results that are not similar to > their preceeding result(s) - I''d imagined he code to do this would be > something along the lines of: > > class Intermediate > def self.results(attributes) > @saved_result = nil > > Connector.each_result(attributes) do |result| > if results_match(result, @saved_result) > @saved_result.merge!(result) > else > yield @saved_result unless @saved_result.nil? > @saved_result = result > end > end > yield @saved_result unless @saved_result.nil? > end > > def results_match(this, last) > return false if last.nil? > .... > end > end > > I can''t for the life of me see how I should spec this though, as trying: > > Connector.stub!(:results).and_yield(@result1, @result2) > > is expecting the two results to be yielded at the same time and not > sequentially.I''m pretty sure you can get what you want by using should_receive instead of stub and doing this: Connector.should_receive(:results).and_yield(@result1) Connector.should_receive(:results).and_yield(@result2)> I can''t see how to stub a method to yield sequential > results so I can spec the behavior for different scenarios of > similarities between subsequent results. Is it possible to do this? > > Any help would be much appreciated > > Cheers > > Rupert > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
rupert
2007-Aug-12 14:23 UTC
[rspec-users] stubbing a method that yeilds sequential results
On 12 Aug 2007, at 14:38, David Chelimsky wrote:> On 8/12/07, rupert <rupert_apsc at rupespad.com> wrote: >> I''ve just found myself stuck trying to rspec something so am hoping >> someone more knowledgable can help. >> >> I have a Connector class which has a class method ''results'' that >> yields results it get from a network service based on a set of >> attributes that I pass to it. I am wanting to yield these results >> from my Intermediate class up to the next ''level'' so the basic no >> frills set up would be this: >> >> class Intermediate >> def self.results(attributes) >> Connector.each_result(attributes) do |result| >> yield result >> end >> end >> end >> >> I''ve worked out how to stub things for the case where the Connector. >> each_result method yields a result once >> >> #setup >> @result = mock("result") >> Connector.stub!(:each_result).and_yield(@result) >> >> @attributes = {} >> @results = [] >> @block = Proc.new { |r| @results << r } >> >> #action >> Intermediate.search_results(@attributes, &@block) >> >> # expectation >> @results.should == [@search_result] >> >> >> However, what I actually need to do is check each result that is >> yielded by the Connector.each_result method and compare it to the >> previous one. If they are sufficiently similar I need to merge them >> (and same again if the next result is sufficiently similar). I only >> want to yeild merged results and results that are not similar to >> their preceeding result(s) - I''d imagined he code to do this would be >> something along the lines of: >> >> class Intermediate >> def self.results(attributes) >> @saved_result = nil >> >> Connector.each_result(attributes) do |result| >> if results_match(result, @saved_result) >> @saved_result.merge!(result) >> else >> yield @saved_result unless @saved_result.nil? >> @saved_result = result >> end >> end >> yield @saved_result unless @saved_result.nil? >> end >> >> def results_match(this, last) >> return false if last.nil? >> .... >> end >> end >> >> I can''t for the life of me see how I should spec this though, as >> trying: >> >> Connector.stub!(:results).and_yield(@result1, @result2) >> >> is expecting the two results to be yielded at the same time and not >> sequentially. > > I''m pretty sure you can get what you want by using should_receive > instead of stub and doing this: > > Connector.should_receive(:results).and_yield(@result1) > Connector.should_receive(:results).and_yield(@result2)Thanks for getting back so quickly - on a Sunday too!! Unfortunately this doesn''t seem to work - it''s failing the second Conector.should_receive spec. This seems to be because the Connector is only receiving the results (sorry for the confusion - it should be each_result) method call once. So what''s happening is that @result1 is being yielded then the second should_receive fails as the method isn''t called a 2nd time. So what I need to be able to say is: Connector.should_receive(:each_result).once.and_yield(@result1 followed_by @result2 followed_by @result3) so one call to a method can yield a set of results in turn. Hope this makes sense (and it''s not just that I''m missing the obvious or doing something stupid!) Cheers Rupert
Mikel Lindsaar
2007-Oct-14 07:28 UTC
[rspec-users] stubbing a method that yeilds sequential results
On 8/13/07, rupert <rupert_apsc at rupespad.com> wrote:> On 12 Aug 2007, at 14:38, David Chelimsky wrote: > >> However, what I actually need to do is check each result that is > >> yielded by the Connector.each_result method and compare it to the > >> previous one. If they are sufficiently similar I need to merge them > > > > I''m pretty sure you can get what you want by using should_receive > > instead of stub and doing this: > > > > Connector.should_receive(:results).and_yield(@result1) > > Connector.should_receive(:results).and_yield(@result2)David, I am having the same mind meld here as Rupert. I am trying to stub out Net::POP3 which yeilds a block for each message returns. The code (snipped down) that I am trying to mock out is: Net::POP3.delete_all(@server, @port, @username, @password) do |m| # do stuff with mail end Doing Net::POP3.should_receive(:delete_all).and_yield(@message1) Works fine. Net::POP3.should_receive(:delete_all).and_yield(@message1) Net::POP3.should_receive(:delete_all).and_yield(@message2) Gives: 1) Spec::Mocks::MockExpectationError in ''GetMail downloading email should download some mail'' Mock ''Class'' expected :delete_all with (any args) once, but received it 0 times ./spec/getmai_spec.rb:86: It is almost like the second mock overrides the first mock ability to intercept the call. Stubbing it does the expected behaviour of overwriting the last stub, ie: Net::POP3.stub!(:delete_all).and_yield(@message1) Net::POP3.stub!(:delete_all).and_yield(@message2) Returns no error, but the block is only called once, not twice. Any further musings on this? Regards Mikel
rupert
2007-Oct-14 12:41 UTC
[rspec-users] stubbing a method that yeilds sequential results
On 14 Oct 2007, at 08:28, Mikel Lindsaar wrote:> On 8/13/07, rupert <rupert_apsc at rupespad.com> wrote: >> On 12 Aug 2007, at 14:38, David Chelimsky wrote: >>>> However, what I actually need to do is check each result that is >>>> yielded by the Connector.each_result method and compare it to the >>>> previous one. If they are sufficiently similar I need to merge >>>> them >>> >>> I''m pretty sure you can get what you want by using should_receive >>> instead of stub and doing this: >>> >>> Connector.should_receive(:results).and_yield(@result1) >>> Connector.should_receive(:results).and_yield(@result2) > > David, I am having the same mind meld here as Rupert. > > I am trying to stub out Net::POP3 which yeilds a block for each > message returns. The code (snipped down) that I am trying to mock out > is: > > Net::POP3.delete_all(@server, @port, @username, @password) do |m| > # do stuff with mail > end > > > Doing > > Net::POP3.should_receive(:delete_all).and_yield(@message1) > > Works fine. > > Net::POP3.should_receive(:delete_all).and_yield(@message1) > Net::POP3.should_receive(:delete_all).and_yield(@message2) > > Gives: > > 1) > Spec::Mocks::MockExpectationError in ''GetMail downloading email should > download some mail'' > Mock ''Class'' expected :delete_all with (any args) once, but > received it 0 times > ./spec/getmai_spec.rb:86: > > > It is almost like the second mock overrides the first mock ability to > intercept the call. > > Stubbing it does the expected behaviour of overwriting the last > stub, ie: > > Net::POP3.stub!(:delete_all).and_yield(@message1) > Net::POP3.stub!(:delete_all).and_yield(@message2) > > Returns no error, but the block is only called once, not twice. > > Any further musings on this?Yep - a patch was assimilated so you should be able to do: Net::POP3.should_receive(:delete_all).and_yield(@message1).and_yield (@message2) which should allow you to call delete_all once and have it yield twice. This is definitely in trunk Cheers Rupert
Mikel Lindsaar
2007-Oct-16 04:50 UTC
[rspec-users] stubbing a method that yeilds sequential results
Nice :) On 10/14/07, rupert <rupert_apsc at rupespad.com> wrote:> > On 14 Oct 2007, at 08:28, Mikel Lindsaar wrote: > > > On 8/13/07, rupert <rupert_apsc at rupespad.com> wrote: > >> On 12 Aug 2007, at 14:38, David Chelimsky wrote: > >>>> However, what I actually need to do is check each result that is > >>>> yielded by the Connector.each_result method and compare it to the > >>>> previous one. If they are sufficiently similar I need to merge > >>>> them > >>> > >>> I''m pretty sure you can get what you want by using should_receive > >>> instead of stub and doing this: > >>> > >>> Connector.should_receive(:results).and_yield(@result1) > >>> Connector.should_receive(:results).and_yield(@result2) > > > > David, I am having the same mind meld here as Rupert. > > > > I am trying to stub out Net::POP3 which yeilds a block for each > > message returns. The code (snipped down) that I am trying to mock out > > is: > > > > Net::POP3.delete_all(@server, @port, @username, @password) do |m| > > # do stuff with mail > > end > > > > > > Doing > > > > Net::POP3.should_receive(:delete_all).and_yield(@message1) > > > > Works fine. > > > > Net::POP3.should_receive(:delete_all).and_yield(@message1) > > Net::POP3.should_receive(:delete_all).and_yield(@message2) > > > > Gives: > > > > 1) > > Spec::Mocks::MockExpectationError in ''GetMail downloading email should > > download some mail'' > > Mock ''Class'' expected :delete_all with (any args) once, but > > received it 0 times > > ./spec/getmai_spec.rb:86: > > > > > > It is almost like the second mock overrides the first mock ability to > > intercept the call. > > > > Stubbing it does the expected behaviour of overwriting the last > > stub, ie: > > > > Net::POP3.stub!(:delete_all).and_yield(@message1) > > Net::POP3.stub!(:delete_all).and_yield(@message2) > > > > Returns no error, but the block is only called once, not twice. > > > > Any further musings on this? > > Yep - a patch was assimilated so you should be able to do: > > Net::POP3.should_receive(:delete_all).and_yield(@message1).and_yield > (@message2) > > which should allow you to call delete_all once and have it yield > twice. This is definitely in trunk > > Cheers > > Rupert > > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >