ChatGPT解决这个技术问题 Extra ChatGPT

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

我正在尝试测试 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 上确实出现了好几次,例如 herehere,普遍的共识是,除非项目要求,否则不要测试日志记录。
艺术,谢谢你的评论。我确实读过那些。这可能是最终的答案。

D
Dennis

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

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

我有类似的情况,期望我的预期字符串是部分字符串,到目前为止我无法弄清楚,如何处理它,有什么帮助吗?
@AmolPujari expect(Rails.logger).to receive(:info).with(/partial_string/) 其中“partial_string”是您要查找的字符串。简单的正则表达式比较
太好了,我正在检查我没有得到 任何东西 记录到错误并检查 Rspec 的任何匹配器都很好地做到了这一点:expect(Rails.logger).to_not receive(:error).with(anything)
“您必须在记录消息之前放置此行”到底是什么意思?期望出现在生成日志的代码之前的代码中?我正在这样做并收到错误,因为记录器正在获取从 it 块甚至运行之前在我的 let 表达式中完成的内容中记录的消息
@sixty4bit 这意味着 expext.. receive 用作事件侦听器-您必须先对其进行设置,然后启动将记录您要捕获的消息的代码
J
Jignesh Gohel

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


a
akira

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

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

我使用了第一种方法,但为什么这个 expect(Rails.logger).to receive(:info).with("A Report processing is already scheduled for: #{report.inspect}") 给出了这个错误:expected: ("A Report processing is already scheduled for: #<Report id: 2677, org_id: 6649, user_id: ...tion: nil, life_state: \"ideation\", chat_link: nil, discarded_at: nil, registrants_count: 2>") ---> got: ("A Report processing is already scheduled for: [#<Report id: 2677, org_id: 6649, user_id:...ion: nil, life_state: \"ideation\", chat_link: nil, discarded_at: nil, registrants_count: 2>]") (1 time)
t
thisismydesign

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

这将使您免于模拟过程并测试消息是否会真正到达它们应该到达的位置(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

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


_from_any_process 是我所缺少的!花了太长时间才找到这个。谢谢!
P
Paulo Fidalgo

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

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


N
Nivetha R

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

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