捕获Ruby Logger输出以进行测试

时间:2014-12-31 05:40:13

标签: ruby logging minitest

我有一个像这样的红宝石课:

require 'logger'
class T
  def do_something
    log = Logger.new(STDERR)
    log.info("Here is an info message")
  end
end

测试脚本排成一行:

#!/usr/bin/env ruby

gem "minitest"
require 'minitest/autorun'

require_relative 't'

class TestMailProcessorClasses < Minitest::Test
  def test_it
    me = T.new

    out, err = capture_io do
      me.do_something
    end

    puts "Out is '#{out}'"
    puts "err is '#{err}'"
  end
end

当我运行此测试时,out和err都是空字符串。我看到消息打印在stderr上(在终端上)。有没有办法让Logger和capture_io很好地一起玩?

我处于直接的Ruby环境中,而不是Ruby on Rails。

5 个答案:

答案 0 :(得分:5)

MiniTest的#capture_io暂时切换$stdout$stderr StringIO个对象,以捕获写入$stdout$stderr的输出。但是Logger有自己对原始标准错误流的引用,它会愉快地写入。我认为你可以认为这是一个错误或至少是MiniTest #capture_io的限制。

在您的情况下,您使用参数Logger在块#capture_io内创建STDERRSTDERR仍然指向原始标准错误流,这就是它无法按预期工作的原因。

STDERR更改为$stderr(其中 指向StringIO个对象)可以解决此问题,但前提是Logger实际上是在#capture_io块中创建,因为在该块之外它指向原始标准错误流。

class T
  def do_something
    log = Logger.new($stderr)
    log.info("Here is an info message")
  end
end

答案 1 :(得分:5)

神奇的是使用capture_subprocess_io

 out, err = capture_subprocess_io do
     do_stuff
 end

答案 2 :(得分:1)

capture_subprocess_io

的文档

基本上伦纳德的例子充实了工作代码并指向文档(不允许编辑!)

out, err = capture_subprocess_io do
  system "echo Some info"
  system "echo You did a bad thing 1>&2"
end

assert_match %r%info%, out
assert_match %r%bad%, err

See Documentation

答案 3 :(得分:0)

这是一个老问题,但我们这样做的一种方法是用期望值模拟记录器。类似的东西

logger.expects(:info).with("Here is an info message")

这允许我们在不改变记录器开箱即用的情况下断言被测代码。

capture_io 为例,我们有一个记录器实现,允许我们传入哈希值并将它们输出到 json。当我们测试该实现时,我们使用 capture_io。这是可能的,因为我们在主题行中使用 $stdout 初始化记录器实现。

subject { CustomLogging.new(ActiveSupport::Logger.new($stdout)) }

在测试中

it 'processes a string message' do
  msg = "stuff"
  out, err = capture_io do
    subject.info(msg)
  end
  out.must_equal "#{msg}\n"
end

答案 4 :(得分:-1)

在初始化StringIO以捕获输出时,您需要提供不同的Logger.new对象,而不是通常指向控制台的STDERR

我稍微修改了上面两个文件,并将其制作成一个文件,以便您可以轻松复制和测试:

require 'logger'
require 'minitest'

class T
  def do_something(io = nil)
    io ||= STDERR
    log = Logger.new io
    log.info("Here is an info message")
  end
end

class TestT < Minitest::Test

  def test_something
    t = T.new
    string_io = StringIO.new
    t.do_something string_io
    puts "Out: #{string_io.string}"
  end

end

Minitest.autorun

说明:

  1. 在没有参数的情况下使用时,方法do_something将在所有其他代码中正常运行。
  2. 当提供StringIO方法时,它使用该方法而不是典型的STDERR,从而能够将输出捕获到文件中,或者在这种情况下进行测试。