I am testing the class which put on the console some messages (with puts, p warnings and etc.). I am just wondering if there is any ability to suppress this output during RSpec tests ?
-
1Architecturally, you may be better off using a logging library or somehow encapsulating the print statements, so that you can toggle printing from some global config. It wouldn't be too hard to search & replace with sed all `print`, `p`, etc. with `Your::Logger.warn` or something. – alexanderbird Feb 24 '16 at 11:22
9 Answers
I suppress puts
output in my classes by redirecting $stout
to a text file. That way, if I need to see the output for any reason, it is there but it doesn't muddy up my test results.
#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
EDIT:
In response to the comment by @MyronMarston, it probably would be smarter to just insert the methods directly into before
and after
as blocks.
#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
It looks a little cleaner and keeps methods off of main
.
Also, note that if you are using Ruby 2.0, you can use __dir__
instead of File.dirname(__FILE__)
.
EDIT2
Also it should be mentioned, that you can forward to true os /dev/null
by using File::NULL
as it was introduced in Ruby v 1.9.3. (jruby 1.7)
Then the code snippet will look as following:
#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

- 348
- 4
- 14

- 16,649
- 4
- 40
- 47
-
4This is a pretty good solution, but bear in mind that it adds `silence_output` and `enable_output` to every object in the system. I think it's fine to do so in small one-off scripts, but otherwise I avoid defining methods on `main`. – Myron Marston Mar 15 '13 at 15:02
-
1@MyronMarston You are right. Though it may not cause problems since it is only meant to be used in RSpec tests, it would be smarter to keep them off of `main`. I added to my answer to reflect this. – Charles Caldwell Mar 15 '13 at 16:14
-
This didn't work for me perfectly. It suppressed some output but not all. I wrote some "puts" within the blocks and they got called several times! – hammady Sep 22 '13 at 07:35
-
@hammady That's odd. I can't think of any reason off the top of my head why it would only capture some of the standard out but not all. I'll try to see if I can duplicate your issue later when I have access to a computer. Let me know if you find out why before then. – Charles Caldwell Sep 22 '13 at 13:17
-
I am using [rjb](https://github.com/arton/rjb) to bind to Java API and sometimes it generates output, could be that rjb overrides $stdout to STDOUT at some point, will check this – hammady Sep 23 '13 at 10:32
-
1Another tip: I use this redirection not in global hooks, but in shared context "mute sterror and stout", with `before(:each) {}` and `after(:each) {}` - in my setup (rspec 3.2) it supresses the output of example while keeping other (more useful in my case) output of a rspec. – Ivan Kolmychek Apr 29 '15 at 10:51
-
This solution doesn't get rid of the "Non US-ASCII detected and no charset defined." output – Jeremy Nov 26 '16 at 02:24
-
For me this didn't work in a plain ruby project. Simply overriding `$stdout` (same for `$stderr`) didn't work. I had to use `reopen`, as e.g. `$stdout.reopen File.new('/dev/null', 'w')` – basiszwo Mar 21 '18 at 14:14
-
1I just discovered with RSpec's new `expect {
}.to output(..).to_stdout` you don't need to redirect output anymore, RSpec will actually capture it and not output! See: https://relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/output-matcher
– Arthur Maltson Jun 13 '18 at 03:31 -
1*****PLEASE NOTE***** If you use pry, I have found this setup to swallow pry output to the console. Is there a fix for this? – Chris Hough Aug 17 '19 at 17:36
-
instead of saving the input and output, you can reset to the default: `$stdout = STDOUT`. – Peter DeWeese Apr 18 '23 at 19:00
Try stubbing methods that make the output in a before block, e.g.
before do
IO.any_instance.stub(:puts) # globally
YourClass.any_instance.stub(:puts) # or for just one class
end
This is explicit, so you won't miss anything you don't want to miss. If you don't care about any output and the method above doesn't work you can always stub the IO object itself:
before do
$stdout.stub(:write) # and/or $stderr if needed
end

- 9,196
- 2
- 25
- 20
-
so simple to just stub out `$stdout` on the tests that I know will write and I don't want them to, thanks :) – sevenseacat Sep 26 '13 at 08:51
An Rspec3.0 Version would be => in spec_helper.rb
RSpec.configure do |c|
c.before { allow($stdout).to receive(:puts) }
end
it will act as before(:each)
but :each is default, so no need to write it explicitly

- 486
- 4
- 9
-
5
-
`allow($stdout).to receive(:puts)` (or `:write`) should be the accepted answer – Yo Ludke Mar 02 '18 at 14:10
Tested with rspec-core (~> 3.4.0)
In describe block you could do
# 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
This way you have the advantage of toggling log output for specific tests. Note, this will not suppress rspec warnings, or messages from rspec.
Another way to disable warnings coming from gems:
add config.warnings = false
to spec_helper
If you wanted to suppress only certain logger methods, like error, info, or warn
you could do
allow_any_instance_of(Logger).to receive(:warn).and_return(nil)
To disable warnings coming from the rspec gem
allow(RSpec::Support).to receive(:warning_notifier).and_return(nil)
but this is generally discouraged because it is meant as a way to let you know you are doing something smelly in your tests.

- 33,652
- 11
- 120
- 99
If you want to suppress output for a single test, there is a more concise way:
it "should do something with printing" do silence_stream(STDOUT) do foo.print.should be_true end end
You may want to change STDOUT
to STDERR
if your test prints an error.

- 5,845
- 3
- 28
- 39
-
3I wanted to note that [silence_stream](http://apidock.com/rails/Kernel/silence_stream) is a part of [activesupport](http://apidock.com/rails/ActiveSupport). – Andrey Chernih Jun 29 '14 at 17:19
-
6[silence_stream is now deprecated](http://www.rubydoc.info/gems/activesupport/4.2.0/Kernel:silence_stream) – dgmstuart Mar 13 '15 at 23:31
Updated answer for Rails 5, in a one-off situation:
before do
RSpec::Mocks.with_temporary_scope do
allow(STDOUT).to receive(:puts)
end
end
You can make this into a method in spec_helper if you'll be doing this a lot.

- 14,494
- 5
- 61
- 97

- 30,257
- 12
- 52
- 74
After trying all of these examples, I ended up using this varation which does not silence or mute binding.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

- 3,389
- 3
- 41
- 80
-
4This is the only solution on this thread that worked for me. And only the stub on `write` was necessary for me. – Matt Apr 26 '20 at 16:36
It can be useful to inject an IO object defaulting to STDOUT. This also makes it easier to assert on the output if you want to.
E.g.
def my_method(arg, io: STDOUT)
io.puts "hello"
arg.reverse
end
And then in your test:
# 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")

- 15,786
- 5
- 82
- 131
You could have the object itself supress based on the environment:
class Foo
def call
puts("blah blah")
# ...
end
private
def puts(msg)
return if ENV['APP_ENV'] == 'test'
super
end
end

- 19,188
- 9
- 91
- 111