【问题标题】:RSpec retry throw exception and then return valueRSpec重试抛出异常然后返回值
【发布时间】:2013-01-08 00:30:35
【问题描述】:

我有一个重试块

 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 不会t 抛出异常。

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

【问题讨论】:

    标签: ruby-on-rails ruby rspec


    【解决方案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 中解救,使用更具体的东西。见:Why is it a bad style to `rescue Exception => e` in Ruby?

    【讨论】:

    • 在里面添加了my_instance,所以你会得到my_instance.stub(:fetch_and_rescan_app_instances)等。像这样调用stub(:fetch_and_rescan_app_instances)是行不通的。
    • 非常感谢! @shioyama,我知道为什么不做救援异常,但是如果我不知道它会抛出什么样的确切异常呢?我知道这是一个网络调用,所以它可能有连接问题或其他任何问题,但不确定它会是什么类型的异常。
    • @Meena,您可以尝试常规救援,它只是从 StandardError 中救援。
    • 请注意,当使用allow 语法(通常可能只是 rspec3)时,您会省略and_returnallow(my_instance).to receive(:fetch_and_rescan_app_instances) do...
    【解决方案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,第二次会返回'some return value'。

    附言。调用多会报错,可以考虑使用不同的接收计数规范https://www.relishapp.com/rspec/rspec-mocks/docs/message-expectations/receive-counts

    【讨论】:

      【解决方案3】:

      这在 RSpec3.x 中有所改变。似乎最好的方法是将一个块传递给定义此类行为的receive

      以下内容来自建议如何创建此类transit failure 的文档:

      (这个错误每隔一段时间它被调用......但很容易适应。)

      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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-12-11
        • 2010-11-06
        • 1970-01-01
        • 2012-03-10
        • 1970-01-01
        • 2012-12-22
        • 2012-06-08
        相关资源
        最近更新 更多