RSpec повторить попытку исключения, а затем вернуть значение - программирование
Подтвердить что ты не робот

RSpec повторить попытку исключения, а затем вернуть значение

У меня есть блок повтора

 def my_method
    app_instances = []
    attempts = 0
    begin 
      app_instances = fetch_and_rescan_app_instances(page_n, policy_id, policy_cpath)
    rescue Exception
      attempts += 1
      retry unless attempts > 2
      raise Exception 
    end
    page_n += 1
  end

где fetch_and_rescan_app_instances получает доступ к сети, поэтому может генерировать исключение.

Я хочу написать rspec-тест, который сначала генерирует исключение, и не генерирует исключение во второй раз, когда он вызывается, поэтому я могу проверить, не повторяет ли второй раз исключение, my_method won ' t бросить exeption.

Я знаю, что могу сделать stub(:fetch_and_rescan_app_instances).and_return(1,3), и первый раз он возвращает 1 и второй раз 3, но я не знаю, как сделать первый раз исключение и вернуть что-то второе.

4b9b3361

Ответ 1

Вы можете вычислить возвращаемое значение в блоке:

describe "my_method" do
  before do
    my_instance = ...
    @times_called = 0
    my_instance.stub(:fetch_and_rescan_app_instances).and_return do
      @times_called += 1
      raise Exception if @times_called == 1
    end
  end

  it "raises exception first time method is called" do
    my_instance.my_method().should raise_exception
  end

  it "does not raise an exception the second time method is called" do
    begin
      my_instance.my_method()
    rescue Exception
    end
    my_instance.my_method().should_not raise_exception
  end
end

Обратите внимание, что вы действительно не должны спасаться от Exception, используйте что-то более конкретное. Смотрите: Почему это плохой стиль для` rescue Exception = > e` в Ruby?

Ответ 2

То, что вы делаете, - это ограничение времени, в течение которого сообщение должно приниматься (получать подсчеты), т.е. в вашем случае вы можете

instance.stub(:fetch_and_rescan_app_instances).once.and_raise(RuntimeError, 'fail')
instance.stub(:fetch_and_rescan_app_instances).once.and_return('some return value')

Вызов instance.fetch_and_rescan_app_instances в первый раз повысит RuntimeError, а второй раз вернет "некоторое возвращаемое значение".

PS. Если вы вызовете больше, это приведет к ошибке, вы можете использовать другую спецификацию счетчика приема https://www.relishapp.com/rspec/rspec-mocks/docs/message-expectations/receive-counts

Ответ 3

Это немного изменилось в RSpec3.x. Кажется, лучший способ - передать блок receive, который определяет этот тип поведения.

В документах говорится о том, как создать этот переходный отказ:

(Это ошибки каждый раз, когда он вызывается... Но легко адаптироваться.)

RSpec.describe "An HTTP API client" do
  it "can simulate transient network failures" do
    client = double("MyHTTPClient")

    call_count = 0
    allow(client).to receive(:fetch_data) do
      call_count += 1
      call_count.odd? ? raise("timeout") : { :count => 15 }
    end

    expect { client.fetch_data }.to raise_error("timeout")
    expect(client.fetch_data).to eq(:count => 15)
    expect { client.fetch_data }.to raise_error("timeout")
    expect(client.fetch_data).to eq(:count => 15)
  end
end