【问题标题】:Suppress console output during RSpec tests在 RSpec 测试期间抑制控制台输出
【发布时间】:2013-03-04 01:23:38
【问题描述】:

我正在测试在控制台上放置一些消息(带有放置、p 警告等)的类。我只是想知道在 RSpec 测试期间是否有任何抑制此输出的能力?

【问题讨论】:

  • 从架构上讲,您最好使用日志库或以某种方式封装打印语句,以便您可以从某些全局配置切换打印。用Your::Logger.warn 或其他东西搜索和替换所有printp 等不会太难。

标签: ruby rspec


【解决方案1】:

尝试在前块中输出的存根方法,例如

before do
  IO.any_instance.stub(:puts) # globally
  YourClass.any_instance.stub(:puts) # or for just one class
end

这是明确的,所以你不会错过任何你不想错过的东西。如果您不关心任何输出并且上述方法不起作用,您可以随时存根 IO 对象本身:

before do
  $stdout.stub(:write) # and/or $stderr if needed
end

【讨论】:

  • 如此简单,只需将 $stdout 在我知道会写但我不希望他们写的测试中存根,谢谢 :)
【解决方案2】:

我通过将$stout 重定向到文本文件来抑制我的课程中的puts 输出。这样,如果我出于任何原因需要查看输出,它就在那里,但不会混淆我的测试结果。

#spec_helper.rb
RSpec.configure do |config|
  config.before(:all, &:silence_output)
  config.after(:all,  &:enable_output)
end

public
# Redirects stderr and stout to /dev/null.txt
def silence_output
  # Store the original stderr and stdout in order to restore them later
  @original_stderr = $stderr
  @original_stdout = $stdout

  # Redirect stderr and stdout
  $stderr = File.new(File.join(File.dirname(__FILE__), 'dev', 'null.txt'), 'w')
  $stdout = File.new(File.join(File.dirname(__FILE__), 'dev', 'null.txt'), 'w')
end

# Replace stderr and stdout so anything else is output correctly
def enable_output
  $stderr = @original_stderr
  $stdout = @original_stdout
  @original_stderr = nil
  @original_stdout = nil
end

编辑:

针对@MyronMarston 的评论,将方法直接插入beforeafter 作为块可能会更聪明。

#spec_helper.rb
RSpec.configure do |config|
  original_stderr = $stderr
  original_stdout = $stdout
  config.before(:all) do 
    # Redirect stderr and stdout
    $stderr = File.new(File.join(File.dirname(__FILE__), 'dev', 'null.txt'), 'w')
    $stdout = File.new(File.join(File.dirname(__FILE__), 'dev', 'null.txt'), 'w')
  end
  config.after(:all) do 
    $stderr = original_stderr
    $stdout = original_stdout
  end
end

它看起来更干净一些,并且使方法远离main。 另外,请注意,如果您使用的是 Ruby 2.0,则可以使用 __dir__ 而不是 File.dirname(__FILE__)

EDIT2

还应该提到的是,您可以使用File::NULL 转发到真正的操作系统/dev/null,因为它是在Ruby v 1.9.3 中引入的。 (jruby 1.7)

然后代码 sn-p 将如下所示:

#spec_helper.rb
RSpec.configure do |config|
  original_stderr = $stderr
  original_stdout = $stdout
  config.before(:all) do
    # Redirect stderr and stdout
    $stderr = File.open(File::NULL, "w")
    $stdout = File.open(File::NULL, "w")
  end
  config.after(:all) do
    $stderr = original_stderr
    $stdout = original_stdout
  end
end

【讨论】:

  • 这是一个很好的解决方案,但请记住,它会将silence_outputenable_output 添加到系统中的每个对象。我认为在小的一次性脚本中这样做很好,但否则我会避免在 main 上定义方法。
  • @MyronMarston 你是对的。尽管它可能不会引起问题,因为它仅用于 RSpec 测试,但将它们远离main 会更聪明。我添加到我的答案以反映这一点。
  • 另一个提示:我不是在全局钩子中使用此重定向,而是在共享上下文“mute sterror and stout”中使用before(:each) {}after(:each) {} - 在我的设置(rspec 3.2)中它抑制了示例的输出,同时保留 rspec 的其他(在我的情况下更有用)输出。
  • 我刚刚发现使用 RSpec 的新 expect { <code with output> }.to output(..).to_stdout 您不再需要重定向输出,RSpec 实际上会捕获它而不是输出!见:relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/…
  • *****请注意***** 如果您使用 pry,我发现此设置可以将 pry 输出吞入控制台。有解决办法吗?
【解决方案3】:

如果你想抑制单个测试的输出,有一种更简洁的方法:

it "should do something with printing" do
  silence_stream(STDOUT) do
    foo.print.should be_true
  end
end

如果您的测试打印错误,您可能需要将 STDOUT 更改为 STDERR

【讨论】:

【解决方案4】:

Rspec3.0 版本将是 => 在 spec_helper.rb 中

RSpec.configure do |c|
  c.before { allow($stdout).to receive(:puts) }
end

它将充当before(:each)

但是 :each 是默认的,所以不需要显式写

【讨论】:

  • 存根 :write 而不是 :puts 为我工作。
  • allow($stdout).to receive(:puts)(或:write)应该是公认的答案
【解决方案5】:

使用 rspec-core (~> 3.4.0) 测试

在描述块中你可以做

# spec_helper.rb
def suppress_log_output
  allow(STDOUT).to receive(:puts) # this disables puts
  logger = double('Logger').as_null_object
  allow(Logger).to receive(:new).and_return(logger)
end

# some_class_spec.rb
RSpec.describe SomeClass do
  before do
    suppress_log_output
  end
end

这样您就可以为特定测试切换日志输出。请注意,这不会抑制 rspec 警告或来自 rspec 的消息。

另一种禁用来自 gem 的警告的方法:

config.warnings = false 添加到 spec_helper

如果您只想抑制某些记录器方法,例如error, info, or warn,您可以这样做

allow_any_instance_of(Logger).to receive(:warn).and_return(nil)

禁用来自 rspec gem 的警告

allow(RSpec::Support).to receive(:warning_notifier).and_return(nil)

但这通常是不鼓励的,因为它是为了让您知道您在测试中做了一些有异味的事情。

【讨论】:

    【解决方案6】:

    在一次性情况下更新了 Rails 5 的答案:

    before do
      RSpec::Mocks.with_temporary_scope do
        allow(STDOUT).to receive(:puts)
      end
    end
    

    如果你经常这样做,你可以把它变成 spec_helper 中的一个方法。

    【讨论】:

      【解决方案7】:

      在尝试了所有这些示例之后,我最终使用了这种 不会使 binding.pry 静音或静音的变体。pry

      # frozen_string_literal: true
      
      RSpec.configure do |config|
        config.before(:each) do
          allow($stdout).to receive(:puts)
          allow($stdout).to receive(:write)
        end
      end
      

      【讨论】:

      • 这是该线程上唯一对我有用的解决方案。我只需要write 上的存根。
      【解决方案8】:

      注入默认为 STDOUT 的 IO 对象会很有用。如果您愿意,这也使得在输出上断言更容易。

      例如

      def my_method(arg, io: STDOUT)
        io.puts "hello"
        arg.reverse
      end
      

      然后在你的测试中:

      # Suppress it.
      
      my_method("hi", io: StringIO.new)
      
      # Assert on it.
      
      io = StringIO.new
      my_method("hi", io: io)
      
      output = io.tap(&:rewind).read
      expect(output).to include("hello")
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-08-07
        • 1970-01-01
        • 2018-04-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多