[rspec-users] stubbing a method that yeilds sequential results

rupert rupert_apsc at rupespad.com
Sun Aug 12 10:23:42 EDT 2007


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


More information about the rspec-users mailing list