Search code examples
rubyunit-testingrspecstub

How to test stubs in failing code?


I have a piece of code that retries a certain API call 3 times before failing. I also have a stub for this API.

I want to write two tests in the case of failure:

  • The whole thing fails;
  • The API has been called three times.

Here is the first test:

it 'fails' do
  expect { risky_call } to raise_error SomeError
end

The second test however, is a bit trickier:

it 'calls the API three times' do
  risky_call
  expect(stub).to have_been_requested.times(3)
end

The problem is that risky_call raises SomeError, making the test fail. I thought of doing something like this:

it 'calls the API three times' do
  risky_call rescue nil
  expect(stub).to have_been_requested.times(3)
end

But it does seem very hackish.

Is there a better/safer/more idiomatic way to write this spec?


Solution

  • Not sure what your stub looks like but this appears to be one of those times where the single expectation per test rule need not apply.

    Your specification is very clear it should try 3 times before raising an error so a test like

    it 'calls the API three times before raising an error' do
      expect { risky_call }.to raise_error(SomeError)
      expect(stub).to have_been_requested.times(3)
    end
    

    Makes logical sense to me. Expecting it to "call 3 times" without context does not. What if it only calls 1 time or 2 times? This makes your test description misleading. The new description explains that it will retry 3 times before an error is raised which is what you actually meant and is easy to understand.