使用ruby将输出抑制到控制台
我正在写一些unit testing,如下所示:
def executing_a_signal a_method(a_signal.new, a_model, a_helper); assert_equal(new_state, a_model.state) end
测试工作正常,但是在断言执行逻辑之前运行的方法主要通过puts
各种消息打印到控制台。
是否有一种快速的,可能是内置的方法来抑制输出到控制台? 我只对该方法对模型对象的最终效果感兴趣,并且为了保持控制台基本清洁,我希望找到一种方法来简单地阻止所有输出到控制台而无需重写或注释掉那些puts
陈述仅仅用于我的测试。
这绝对不是一个关键问题,但非常希望听到任何想法或想法(或解决方法)。
我在测试中使用以下代码段来捕获和测试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
有两种解决方案:重定向puts
写入的位置(上面的@cldwalker给出的解决方案),或者将puts
方法本身覆盖为无操作。 (实现应该是显而易见的: module Kernel; def puts(*args) end end
)。
但是,在这种情况下, 真正最好的解决方案是“倾听您的测试”。 因为,通常当某些东西难以测试时,您的测试确实试图告诉您设计出了问题。 在这种情况下,我闻到违反单一责任原则:为什么模型对象需要知道如何写入控制台? 它的职责是代表域概念,而不是记录! 这就是Logger对象的用途!
因此,另一种解决方案是让Model对象委派记录到Logger对象的责任,并使用dependency injection为Model对象注入合适的Logger对象。 这样,你可以简单地为测试注入假记录器。 这是一个例子:
#!/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对象应该将它记录的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
对@ cldwalker的解决方案略微更清晰:
def silenced $stdout = StringIO.new yield ensure $stdout = STDOUT end silenced do something_that_prints end
reopen '/dev/null'
另一种选择是使用以下方法重定向到/dev/null
:
STDOUT.reopen('/dev/null', 'w') STDERR.reopen('/dev/null', 'w')
此技术用于stdlib(切换源)的WEBrick::Daemon
。
它的优点是比StringIO.new
更有效,因为它不会将stdout存储在StringIO
,但它的可移植性较差。
我只是在我的.rb文件的开头使用了下面的代码..所以它禁用所有控制台打印语句..
def puts(*args) end