【问题标题】:suppresing output to console with ruby用红宝石抑制输出到控制台
【发布时间】:2010-12-02 12:48:25
【问题描述】:

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

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

测试工作正常,但是在断言之前运行以执行逻辑的方法会向控制台打印各种消息,主要是通过puts

是否有一种快速的(可能是内置的)方法来抑制向控制台输出?我只对该方法对模型对象的最终效果感兴趣,并且为了基本保持控制台清洁,我希望找到一种方法来简单地阻止所有输出到控制台而不重写或注释掉那些puts 语句仅用于我的测试。

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

【问题讨论】:

    标签: ruby console suppress-warnings


    【解决方案1】:

    我在测试中使用下面的sn-p来捕获和测试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
    

    【讨论】:

    • 谢谢,这大概是说没有标准的内置方式吧?
    • 我在 Ruby/Rake 中维护构建脚本,这个技巧甚至在使用 Ruby sh 函数调用时禁用了 devenv (Visual Studio) 的控制台输出。非常感谢!
    • 如果您在测试代码的任何地方使用STDOUT 常量,则必须单独重新定义。
    • 这太棒了。出于某种原因,我们正在使用的 gem 会不断生成输出(可能有人在其中留下了 put),这是一种无需猴子修补 gem 的简单方法。谢谢!
    【解决方案2】:

    有两种解决方案:重定向puts 写入的位置(上面@cldwalker 给出的解决方案),或者将puts 方法本身覆盖为无操作。 (实现应该很明显:module Kernel; def puts(*args) end end)。

    但是,在这种情况下,真正最好的解决方案是“听你的测试”。因为,通常当某些东西难以测试时,您的测试实际上是在试图告诉您您的设计有问题。在这种情况下,我闻到了对单一职责原则的违反:为什么模型对象需要知道如何写入控制台?它的职责是代表一个领域概念,而不是记录!这就是 Logger 对象的用途!

    因此,另一种解决方案是让 Model 对象将日志记录的责任委托给 Logger 对象,并使用依赖注入向 Model 对象注入合适的 Logger 对象。这样,您可以简单地为测试注入一个 fake 记录器。这是一个例子:

    #!/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
    

    至少,模型对象应该将它正在记录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
    

    【讨论】:

      【解决方案3】:

      reopen '/dev/null'

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

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

      此技术用于标准库的WEBrick::Daemon(切换源代码)。

      它的优点是比StringIO.new 更高效,因为它不会将标准输出存储在StringIO 上,但它的可移植性较差。

      【讨论】:

        【解决方案4】:

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

        def silenced
          $stdout = StringIO.new
        
          yield
        ensure
          $stdout = STDOUT
        end
        
        silenced do
          something_that_prints
        end
        

        【讨论】:

          【解决方案5】:

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

           def puts(*args) end
          

          【讨论】:

            猜你喜欢
            • 2011-02-08
            • 1970-01-01
            • 2012-06-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多