23

我有一个重试块

 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 不会抛出例外。

我知道我可以做到stub(:fetch_and_rescan_app_instances).and_return(1,3),第一次它返回 1,第二次返回 3,但我不知道如何第一次抛出异常并第二次返回一些东西。

4

3 回答 3

24

您可以在一个块中计算返回值:

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,使用更具体的东西。请参阅:为什么在 Ruby 中 `rescue Exception => e` 是一种不好的风格?

于 2013-01-08T02:25:07.693 回答
21

您所做的是限制应该接收消息的时间(接收计数),即在您的情况下,您可以

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

于 2013-09-10T10:16:08.220 回答
11

这在 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
于 2016-10-24T12:52:20.613 回答