RSpec:如何测试Rails记录器消息期望?

时间:2012-06-12 13:54:34

标签: logging rspec mocking stubbing

我正在尝试测试Rails记录器在我的一些规范中接收消息。我使用的是Logging gem

假设我有一个这样的课程:

class BaseWorker

  def execute
    logger.info 'Starting the worker...'
  end

end

一个规范如:

describe BaseWorker do

  it 'should log an info message' do
    base_worker = BaseWorker.new
    logger_mock = double('Logging::Rails').as_null_object
    Logging::Rails.stub_chain(:logger, :info).and_return(logger_mock)

    logger_mock.should_receive(:info).with('Starting the worker...')
    base_worker.execute
    Logging::Rails.unstub(:logger)
  end

end

我收到以下失败消息:

 Failure/Error: logger_mock.should_receive(:info).with('Starting worker...')
   (Double "Logging::Rails").info("Starting worker...")
       expected: 1 time
       received: 0 times

我尝试了几种不同的方法来让规范通过。这适用于例如:

class BaseWorker

  attr_accessor :log

  def initialize
    @log = logger
  end

  def execute
    @log.info 'Starting the worker...'
  end

end

describe BaseWorker do
  it 'should log an info message' do
    base_worker = BaseWorker.new
    logger_mock = double('logger')
    base_worker.log = logger_mock

    logger_mock.should_receive(:info).with('Starting the worker...')
    base_worker.execute
  end
end

但是必须像这样设置一个可访问的实例变量似乎尾巴在这里摇摆着。 (实际上,我甚至不确定为什么将记录器复制到@log会使它通过。)

测试日志记录的好方法是什么?

6 个答案:

答案 0 :(得分:98)

虽然我同意您通常不想测试记录器,但有时它可能有用。

我对Rails.logger的期望取得了成功。

使用RSpec不推荐使用的should语法:

Rails.logger.should_receive(:info).with("some message")

使用RSpec较新的expect语法:

expect(Rails.logger).to receive(:info).with("some message")

注意:在控制器和型号规格中,您必须在记录消息之前将此行放入。如果你把它放在后面,你会收到如下错误信息:

Failure/Error: expect(Rails.logger).to receive(:info).with("some message")
       (#<ActiveSupport::Logger:0x007f27f72136c8>).info("some message")
           expected: 1 time with arguments: ("some message")
           received: 0 times

答案 1 :(得分:15)

使用RSpec 3+版本

包含Rails.logger.error 的单一调用的实际代码:

Rails.logger.error "Some useful error message"

规格代码:

expect(Rails.logger).to receive(:error).with(/error message/)

如果您希望在规范运行时实际记录错误消息,请使用以下代码:

expect(Rails.logger).to receive(:error).with(/error message/).and_call_original

包含Rails.logger.error 多次调用的实际代码:

Rails.logger.error "Technical Error Message"
Rails.logger.error "User-friendly Error Message"

规格代码:

expect(Rails.logger).to receive(:error).ordered
expect(Rails.logger).to receive(:error).with(/User-friendly Error /).ordered.and_call_original

此外,如果您只关心匹配第一条消息而不关注任何后续消息,那么您可以使用以下

  expect(Rails.logger).to receive(:debug).with("Technical Error Message").ordered.and_call_original
  expect(Rails.logger).to receive(:debug).at_least(:once).with(instance_of(String)).ordered

请注意,上面的变体设置.ordered非常重要,否则期望设置开始失败。

参考文献:

http://www.relishapp.com/rspec/rspec-mocks/v/3-4/docs/setting-constraints/matching-arguments

http://www.relishapp.com/rspec/rspec-mocks/v/3-4/docs/setting-constraints/message-order

答案 2 :(得分:5)

如果您的目标是测试日志记录功能,您还可以考虑验证输出到标准流。

这将使您免受模拟过程的影响,并测试消息是否会实际结束(STDOUT / STDERR)。

使用RSpec&#39; output matcher(在3.0中引入),您可以执行以下操作:

expect { my_method }.to output("my message").to_stdout
expect { my_method }.to output("my error").to_stderr

对于LoggerLogging等库,您可能必须使用output.to_<>_from_any_process

答案 3 :(得分:4)

如果要保持测试的一致性,但最后设置期望值,则需要添加设置:

minimizer

答案 4 :(得分:3)

而不是在之前使用此行,而是记录消息:

expect(Rails.logger).to receive(:info).with("some message")
something that triggers the logger...

您可以将Rails记录器设置为间谍,并改为使用have_received

allow(Rails.logger).to receive(:info).at_least(:once)

something that triggers the logger...

expect(Rails.logger).to have_received(:info).with("some message").once

答案 5 :(得分:0)

即使我有非常相似的错误:

Error: Custom { kind: InvalidData, error: WebPKIError(CAUsedAsEndEntity) }

以下内容对我有用

Failure/Error: expect(Rails.logger).to receive(:info).with("some message")
       (#<ActiveSupport::Logger:0x007f27f72136c8>).info("some message")
           expected: 1 time with arguments: ("some message")
           received: 0 times

参考:https://relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/output-matcher