使用 ruby 抑制输出到控制台

问题描述 投票:0回答:5

我正在编写一些单元测试,如下所示:

def executing_a_signal
  a_method(a_signal.new, a_model, a_helper);
  assert_equal(new_state, a_model.state)
end

测试工作正常,但在断言执行逻辑之前运行的方法主要通过

puts
将各种消息打印到控制台。

是否有一种快速的(也许是内置的)方法来抑制控制台的输出?我只对该方法对模型对象的最终效果感兴趣,并且为了基本上保持控制台干净,我希望找到一种方法来简单地阻止所有输出到控制台,而无需重写或注释掉那些

puts
陈述仅供我的测试使用。

这绝对不是一个关键问题,但非常希望听到对此的任何想法或想法(或解决方法)。

ruby testing console suppress-warnings
5个回答
34
投票

我在测试中使用以下代码片段来捕获和测试 STDOUT

def capture_stdout(&block)
  original_stdout = $stdout
  $stdout = fake = StringIO.new
  begin
    yield
  ensure
    $stdout = original_stdout
  end
  fake.string
end

用这个方法,上面的内容就变成了:

def executing_a_signal
  capture_stdout { a_method(a_signal.new, a_model, a_helper) }
  assert_equal(new_state, a_model.state)
end

11
投票

@cldwalker 的解决方案稍微干净一些:

def silenced
  $stdout = StringIO.new

  yield
ensure
  $stdout = STDOUT
end

silenced do
  something_that_prints
end

4
投票

有两种解决方案:重定向

puts
写入的位置(上面@cldwalker给出的解决方案),或者覆盖
puts
方法本身以使其成为无操作。 (实施应该是显而易见的:
module Kernel; def puts(*args) end end
)。

但是,在这种情况下,“真正”最好的解决方案是“听取您的测试”。因为,通常当某些东西很难测试时,你的测试实际上是在试图告诉你你的设计有问题。在这种情况下,我感觉到违反了单一职责原则:为什么模型对象需要知道如何写入控制台?它的职责是代表领域概念,而不是记录!这就是 Logger 对象的用途! 因此,另一种解决方案是让 Model 对象委托将日志记录到 Logger 对象的责任,并使用依赖项注入将合适的 Logger 对象注入 Model 对象。这样,您只需注入一个“假”记录器即可进行测试。这是一个例子:

#!/usr/bin/env ruby class SomeModel def initialize(logger=Kernel) @logger = logger end def some_method_that_logs; @logger.puts 'bla' end end require 'test/unit' require 'stringio' class TestQuietLogging < Test::Unit::TestCase def setup; @old_stdout, $> = $>, (@fake_logdest = StringIO.new) end def teardown; $> = @old_stdout end def test_that_default_logging_is_still_noisy SomeModel.new.some_method_that_logs assert_equal "bla\n", @fake_logdest.string end def test_that_logging_can_be_made_quiet fake_logger = Object.new def fake_logger.puts *args; end SomeModel.new(fake_logger).some_method_that_logs assert_equal '', @fake_logdest.string end end 至少,Model 对象应该将其记录为

to
IO

对象作为参数,这样您就可以简单地将

StringIO.new
注入其中进行测试: #!/usr/bin/env ruby class SomeModel def initialize(logdest=$>) @logdest = logdest end def some_method_that_logs; @logdest.puts 'bla' end end require 'test/unit' require 'stringio' class TestQuietLogging < Test::Unit::TestCase def setup; @old_stdout, $> = $>, (@fake_logdest = StringIO.new) end def teardown; $> = @old_stdout end def test_that_default_logging_is_still_noisy SomeModel.new.some_method_that_logs assert_equal "bla\n", @fake_logdest.string end def test_that_logging_can_be_made_quiet fake_logdest = (@fake_logdest = StringIO.new) SomeModel.new(fake_logdest).some_method_that_logs assert_equal '', @fake_logdest.string assert_equal "bla\n", fake_logdest.string end end

如果您仍然希望能够在模型中直接说出

puts whatever
 或者您担心有人可能会忘记在记录器对象上调用 
puts

,您可以提供自己的(私有)puts 方法:


class SomeModel def initialize(logdest=$>) @logdest = logdest end def some_method_that_logs; puts 'bla' end private def puts(*args) @logdest.puts *args end end
    


1
投票

另一种选择是重定向到 
/dev/null

STDOUT.reopen('/dev/null', 'w') STDERR.reopen('/dev/null', 'w')


此技术用于 stdlib 的

WEBrick::Daemon

(切换源)。

它的优点是比 
StringIO.new 更高效,因为它不会将标准输出存储在

StringIO

上,但它的可移植性较差。

    
我只是在 .rb 文件的开头使用了以下代码..因此它禁用了所有控制台打印语句..


0
投票

© www.soinside.com 2019 - 2024. All rights reserved.