如何在 rspec 中测试 rescue 子句中的代码

How to test code in rescue clause in rspec

我的代码中有以下模式:

重点是我们需要静默记录错​​误,但如果代码中有错误,测试需要失败。

begin
  self.a_method_call
  some_other_object.a_method_that_has_been_refactored
rescue StandardError => e
  Rails.logger.error e.backtrace
end

如果由于 self.a_method_callsome_other_object.a_method_that_has_been_refactored 之间的交互而引发错误,则救援标准错误将消除任何错误并通过任何测试代码块的测试。如果 begin 子句中的代码有错误,我如何才能停止救援以便测试失败?

错误记录是其功能的一部分。不要让救援沉默,而是捕获错误日志记录。

正如所写,你可以通过 mocking the logger. Use with to configure what arguments you expect Rails.logger.error to receive. Since you don't know exactly what will be received, you can use various matchers like instance_of 来检查你得到了什么 backtrace returns,一个数组。

it 'logs the backtrace as an error' do
  # This comes before you call the method to set up the mock which expects
  # to be called.
  expect(Rails.logger).to receive(:error)
    .with(instance_of(Array))  

  thing.some_method
end

因为这会替换 Rails.logger,如果 thing.some_method 调用 Rails.logger 的过程中有任何其他内容,测试将失败。

我们可以通过小的重构使这更容易。不要直接使用 Rails.logger,而是将其设为属性。

class SomeClass
  attr_accessor :logger

  def initialize
    @logger = Rails.logger
  end

  def some_method
    a_method_call
    some_other_object.a_method_that_has_been_refactored
  rescue StandardError => e
    logger.error e.backtrace
  end
end

现在我们可以专门模拟从 thing.logger 返回的内容。

it 'logs the backtrace as an error' do
  # This comes before you call the method to set up the mock which expects
  # to be called.
  expect(thing.logger).to receive(:error)
    .with(instance_of(Array))  

  thing.some_method
end