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

我正在尝试测试 Rails 日志记录器是否在我的一些规范中接收消息。我正在使用 伐木宝石

假设我有一门这样的课:

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

但是像这样设置一个方便的实例变量看起来就像是在摇尾巴。(实际上,我甚至不确定为什么把 logger 复制到@log 会让它通过。)

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

57157 次浏览

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

我对 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

使用 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

如果你的目标是测试日志功能,你也可以考虑验证输出的文本数据流。

这将省去您的嘲笑过程,并测试消息是否真的会到达它们应该到达的地方(STDOUT/STDERR)。

使用 RSpec 的 输出匹配器输出匹配器(在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

不使用这一行 之前,而是记录消息:

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

就连我也犯过类似的错误:

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

下面这些对我很有用,

expect { my_method }.
to output(/error messsage/).to_stdout_from_any_process

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

如果你想在你的测试中保持一致性,但是最终你需要在你的设置中添加期望值:

setup do
allow(Rails.logger).to receive(:info)
end
...


it 'should log an info message' do
{code}


expect(Rails.logger).to have_received(:info).with('Starting the worker...')
end