Stub instance method different return value on second call using minitest

8.3k views Asked by At

I am paginating a feed of a user and would like to mock the responses of the API I am using. The API can return strange results so I want to make sure that if the API returns items that I have already seen, stop paginating. I have used minitest to stub the first time the method get_next_page is called but I would like to stub it the second and third time it is called with different values.

Should I just use rSpec? newbie to ruby ...

Here is the snippet

test "crawler does not paginate if no new items in next page" do
    # 1: A, B
    # 2: B, D => D
    # 3: A => stop
    crawler = CrawlJob.new
    first_page = [
        {"id"=> "item-A"},
        {"id"=> "item-B"}
    ]
    second_page = [
        {"id"=> "item-B"},
        {"id"=> "item-D"}
    ]
    third_page = [{"id"=> "item-A"}]

    # can only stub with same second page
    # but I want to respond with third page
    # the second time get_next_page is called
    crawler.stub :get_next_page, second_page do
        items, times_paginated = crawler.paginate_feed(first_page)
        assert times_paginated == 3
    end
end
3

There are 3 answers

0
zetetic On

I don't know about Minitest, but RSpec is able to return a different value each time the method is invoked by specifying multiple return values in and_return.

allow(crawler).to receive(:get_next_page).and_return(first_page, second_page, third_page)
0
Anthony On

I'm sure you have this figured out by now but ...

I had to use mocha a mocking framework to get this to work.

Then I was able to do this:

 Object.any_instance.stubs(:get_value).returns('a','b').then.returns('c')
2
tsamb On

Try supplying a proc or lambda to your stub instead of a value, e.g.

return_values = ["returned first", "returned second", "returned third"]
some_object.stub :method_to_stub, proc { return_values.shift }

Upon inspection of Minitest's source code, the stub method takes either a value or a callable object as a second argument.

You can take advantage of this behavior by using a proc or lambda to shift (or pop) values from a predefined array of return values.

So in your case, you could:

  1. wrap your already defined second_page and third_page variables in an array, then
  2. pass a lambda as #stub's second argument which destructively removes and returns the first element of the array each time the stubbed method is called.

Example:

return_values = [second_page, third_page]
crawler.stub :get_next_page, ->{ return_values.shift } do
    items, times_paginated = crawler.paginate_feed(first_page)
    assert times_paginated == 3
end