捕获Ruby Logger输出以进行测试

2ledvvac  于 12个月前  发布在  Ruby
关注(0)|答案(6)|浏览(146)

我有一个这样的Ruby类:

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。

vi4fp9gy

vi4fp9gy1#

神奇的是使用capture_subprocess_io

out, err = capture_subprocess_io do
     do_stuff
 end

字符串

b09cbbtk

b09cbbtk2#

MiniTest的#capture_io临时切换$stdout$stderrStringIO对象,以捕获写入$stdout$stderr的输出。但Logger有自己的原始标准错误流引用,它会愉快地写入。我认为你可以认为这是一个bug,或者至少是MiniTest的#capture_io的限制。
在您的例子中,您正在使用参数STDERR在块内创建Logger#capture_ioSTDERR仍然指向原始标准错误流,这就是为什么它不能按预期工作的原因。
STDERR更改为$stderr(此时 * 确实 * 指向StringIO对象)可以解决这个问题,但只有当Logger实际上是在#capture_io块中创建的,因为在该块之外,它指向原始的标准错误流。

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

字符串

bzzcjhmw

bzzcjhmw3#

capture_subprocess_io文档

基本上,伦纳德的例子用工作代码和指向文档来充实和注解。
将$stdout和$stderr捕获到字符串中,使用Tempfile确保子进程IO也被捕获。

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

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

字符串
注意:这个方法比#capture_io慢大约10倍,所以只在需要测试子进程的输出时使用。
See Documentation

uurv41yg

uurv41yg4#

很晚了,但这是对先前答案的回应:
https://stackoverflow.com/a/27717477/19637807
在利用StringIO的基础上,我们可以使用minitest和mocha来做这样的事情:

log_output_capture = StringIO.new
    logger = Logger.new(log_output_capture)
    Logger.stubs(:new).returns(logger)

字符串
这确保了在被调用用于测试的函数中的任何Logger.new示例化都将被StringIO示例替换用于输出。因此,您根本不需要修改底层函数的日志记录器实现!
我使用Jikku的答案成功地对一个ruby lambda函数进行了单元测试,评估了它的预期日志输出:

# after stubbing Logger.new

    lambda_handler(event: @event, context: @context)
    puts log_output_capture.string

    expected_log_output = File.read("expected_outputs/lambda_run.log")
    assert_match(expected_log_output, log_output_capture.string )

oipij1gg

oipij1gg5#

这是一个老问题,但我们这样做的一种方法是用一个expects来模拟记录器。
第一个月
这允许我们Assert测试中的代码,而不改变logger的工作方式。
作为capture_io的一个例子,我们有一个logger实现,允许我们传入哈希值并将其输出到json。当我们测试该实现时,我们使用capture_io。这是可能的,因为我们在主题行中使用$stdout初始化了logger实现。
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

字符串

iyzzxitl

iyzzxitl6#

在初始化Logger.new以捕获输出时,您需要提供一个不同的StringIO对象,而不是通常的: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在没有参数的情况下使用时,将在所有其他代码中正常运行。
1.当提供StringIO方法时,它使用该方法而不是典型的STDERR,从而能够捕获输出,例如将其捕获到文件中或在本例中用于测试。

相关问题