rspec / rspec-mocks

RSpec's 'test double' framework, with support for stubbing and mocking
https://rspec.info
MIT License
1.16k stars 358 forks source link

Allow a same method call raise an exception and return a value on the following call. #1319

Closed bvicenzo closed 4 years ago

bvicenzo commented 4 years ago

Subject of the issue

I'm trying to implement a method that retries to save a record into the database when this exception is raised. Then, I'm trying to create a scenario where the first save call raises that exception and the second one returns true and persists the record.

Your environment

Steps to reproduce

require 'rspec'

DatabaseError = Class.new(RuntimeError)

class Foo
  ATTEMPTS = 2

  def bar
    @attempt ||= 1
    baz
  rescue DatabaseError
    if @attempt <= attempts
      @attempt += 1
      retry
    else
      # Do Something to finsh attempts
    end
  end

  def baz
    # Database Call Which can raise an error
    nil
  end
end

RSpec.describe Foo do
  describe '#bar' do
    subject(:foo) { described_class.new }

    context 'trying with 2 allows' do
      before do
        allow(foo).to receive(:baz).and_raise(DatabaseError)
        allow(foo).to receive(:baz).and_return(true)
      end

      it 'retries the operation' do
        expect(foo).to receive(:baz).exactly(2).times

        foo.bar
      end
    end

    context 'trying with just one allow' do
      before do
        allow(foo).to receive(:baz).and_raise(DatabaseError).and_return(true)
      end

      it 'retries the operation' do
        expect(foo).to receive(:baz).exactly(2).times

        foo.bar
      end
    end
  end
end

RSpec::ExampleGroups::Foo.run(RSpec.configuration.reporter)

Expected behavior

The test pass with the method #baz being called 2 times.

Actual behavior

Test break because just the mock that returns true is executed.

JonRowe commented 4 years ago

We support providing your own implementation:

times = 0
allow(foo).to receive(:baz) do
  return true if times > 0
  times +=1
  raise DatabaseError
end
bvicenzo commented 4 years ago

@JonRowe Ok. Thanks.