我正在测试一个会在控制台输出一些信息的类(使用puts、p或warnings等)。 我想知道在 RSpec 测试期间是否有任何能力抑制此输出?
我通过将$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的评论,更明智的做法可能是将方法直接插入before
和after
作为块。
#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__)
。
编辑2
还应该提到,您可以通过使用File::NULL
将输出重定向到真正的/dev/null
,
这是在Ruby v 1.9.3(jruby 1.7)中引入的。
然后代码段将如下所示:
#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_output
和enable_output
。我认为在编写小型一次性脚本时这样做没问题,但除此之外,我避免在main
上定义方法。 - Myron Marstonmain
中排除。我更新了我的答案以反映这一点。 - Charles Caldwellbefore(:each){}
和after(:each){}
-在我的设置中(rspec 3.2),它抑制了示例的输出,同时保留了其他(在我的情况下更有用的)rspec输出。 - Ivan Kolmychekexpect { <code with output> }.to output(..).to_stdout
,你不再需要重定向输出,RSpec 实际上会捕获它并且不输出!请参见:https://relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/output-matcher - Arthur Maltson尝试为在before块中输出的方法进行存根处理,例如:
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
,谢谢 :) - sevenseacat一个 Rspec3.0 版本将会在 spec_helper.rb 中。
RSpec.configure do |c|
c.before { allow($stdout).to receive(:puts) }
end
它将作为before(:each)
的作用
但是:each是默认值,所以不需要显式地编写它
:write
而不是 :puts
进行存根测试对我很有效。 - mc9allow($stdout).to receive(:puts)
(或:write
)应该是被接受的答案。 - Yo Ludke测试使用 rspec-core(约为 3.4.0)
在 describe 块中,您可以这样做:
# 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警告或消息。
另一种禁用来自gem的警告的方法:
将config.warnings = false
添加到spec_helper中
如果您想要抑制仅限于某些记录器方法(例如error、info或warn
),则可以执行以下操作
allow_any_instance_of(Logger).to receive(:warn).and_return(nil)
要禁用来自rspec gem的警告,请执行以下操作
allow(RSpec::Support).to receive(:warning_notifier).and_return(nil)
但是,通常不建议这样做,因为它旨在让您知道在测试中存在问题。
it "should do something with printing" do silence_stream(STDOUT) do foo.print.should be_true end end
如果您的测试打印了错误信息,您可能希望将
STDOUT
更改为STDERR
。
silence_stream
现已弃用。 - dgmstuart以下是针对Rails 5的更新答案,适用于单次操作:
before do
RSpec::Mocks.with_temporary_scope do
allow(STDOUT).to receive(:puts)
end
end
# 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
桩代码是必需的。 - Matt你可以让对象根据环境自行抑制:
class Foo
def call
puts("blah blah")
# ...
end
private
def puts(msg)
return if ENV['APP_ENV'] == 'test'
super
end
end
在编程中,注入一个默认为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")
sed
可以轻松地将所有的print
、p
等用Your::Logger.warn
或其他内容进行搜索和替换。 - alexanderbird