RSpec:如何测试 Rails 记录器消息期望?
Posted
技术标签:
【中文标题】RSpec:如何测试 Rails 记录器消息期望?【英文标题】:RSpec: how to test Rails logger message expectations? 【发布时间】:2012-06-15 10:10:24 【问题描述】:我正在尝试测试 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
但是必须设置一个这样的可访问实例变量似乎在这里摇摆不定。 (实际上,我什至不确定为什么将 logger 复制到 @log 会使其通过。)
什么是测试日志记录的好解决方案?
【问题讨论】:
这个问题在 SO 上确实出现了好几次,例如参见 here 和 here,普遍的共识是除非项目要求,否则不要测试日志记录。 艺术,感谢您的评论。我确实读过那些。这可能是最终的答案。 【参考方案1】:虽然我同意您通常不想测试记录器,但有时它可能很有用。
我在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
【讨论】:
我有类似的情况,期望我的预期字符串是部分字符串,到目前为止我无法弄清楚,如何处理它,有什么帮助吗? @AmolPujariexpect(Rails.logger).to receive(:info).with(/partial_string/)
其中“partial_string”是您要查找的字符串。简单的正则表达式比较
这太好了,我正在检查我没有得到 任何东西 记录到错误并检查 Rspec 的任何匹配器都可以很好地做到这一点:expect(Rails.logger).to_not receive(:error).with(anything)
“您必须在记录消息之前放置此行”到底是什么意思?期望出现在生成日志的代码之前的代码中?我正在这样做并得到一个错误,因为记录器正在获取从我的 let
表达式中正在完成的事情记录的消息,然后 it
块甚至运行
@sixty4bit 这意味着,expext..receive 作为事件监听器工作 - 您必须先设置它,然后启动将记录您想要捕获的消息的代码【参考方案2】:
使用 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
【讨论】:
【参考方案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
【讨论】:
【参考方案4】:如果您的目标是测试日志记录功能,您还可以考虑验证输出到标准流。
这将使您免于模拟过程并测试消息是否会真正到达它们应该到达的位置(STDOUT/STDERR)。
使用 RSpec 的 output matcher(在 3.0 中引入),您可以执行以下操作:
expect my_method .to output("my message").to_stdout
expect my_method .to output("my error").to_stderr
对于Logger
或Logging
等库,您可能必须使用output.to_<>_from_any_process
。
【讨论】:
_from_any_process
是我所缺少的!花了太长时间才找到这个。谢谢!【参考方案5】:
如果您想在测试中保持一致性,但最后设置期望值,您需要在设置中添加:
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
【讨论】:
【参考方案6】:即使我也有非常相似的错误:
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
【讨论】:
以上是关于RSpec:如何测试 Rails 记录器消息期望?的主要内容,如果未能解决你的问题,请参考以下文章
ruby 具有期望语法的Rspec Rails备忘单(包括capybara匹配器)