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

ker*_*lin 54 logging rspec mocking stubbing

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

假设我有一个这样的类:

class BaseWorker

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

end
Run Code Online (Sandbox Code Playgroud)

一个规格如:

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
Run Code Online (Sandbox Code Playgroud)

我收到以下失败消息:

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

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

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
Run Code Online (Sandbox Code Playgroud)

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

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

Tan*_*ili 112

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

我已经取得了预期的成功Rails.logger.

使用RSpec的弃用should语法:

Rails.logger.should_receive(:info).with("some message")
Run Code Online (Sandbox Code Playgroud)

使用RSpec的新expect语法:

expect(Rails.logger).to receive(:info).with("some message")
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

  • 究竟是什么意思"你必须在记录消息之前放置这一行"?期望出现在生成日志的代码之前的代码中?我正在这样做并得到一个错误,因为记录器在`it`块甚至运行之前获取从我的`let`表达式中完成的东西记录的消息 (4认同)
  • @AmolPujari`respect(Rails.logger).接收(:info).with(/ partial_string /)`其中"partial_string"是您正在寻找的字符串.简单的正则表达式比较 (3认同)
  • @ sixty4bit就意味着,exxt .. receive作为一个事件监听器 - 你必须先设置它然后启动将记录你要捕获的消息的代码 (2认同)

Jig*_*hel 18

使用RSpec 3+版本

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

Rails.logger.error "Some useful error message"
Run Code Online (Sandbox Code Playgroud)

规格代码:

expect(Rails.logger).to receive(:error).with(/error message/)
Run Code Online (Sandbox Code Playgroud)

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

expect(Rails.logger).to receive(:error).with(/error message/).and_call_original
Run Code Online (Sandbox Code Playgroud)

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

Rails.logger.error "Technical Error Message"
Rails.logger.error "User-friendly Error Message"
Run Code Online (Sandbox Code Playgroud)

规格代码:

expect(Rails.logger).to receive(:error).ordered
expect(Rails.logger).to receive(:error).with(/User-friendly Error /).ordered.and_call_original
Run Code Online (Sandbox Code Playgroud)

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

  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
Run Code Online (Sandbox Code Playgroud)

注意上面的变化设置.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


aki*_*ira 10

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

expect(Rails.logger).to receive(:info).with("some message")
something that triggers the logger...
Run Code Online (Sandbox Code Playgroud)

您可以将 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
Run Code Online (Sandbox Code Playgroud)


Pau*_*lgo 8

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

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

Run Code Online (Sandbox Code Playgroud)


thi*_*ign 7

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

这将使您免于模拟过程并测试消息是否真的会在他们应该的地方结束(STDOUT/STDERR)。

使用 RSpec 的输出匹配器(在 3.0 中引入),您可以执行以下操作:

expect { my_method }.to output("my message").to_stdout
expect { my_method }.to output("my error").to_stderr
Run Code Online (Sandbox Code Playgroud)

对于诸如Logger或之类的库,Logging您可能必须使用output.to_<>_from_any_process.