Testing STDOUT output in Rspec
RSpec 3.0+
RSpec 3.0 added a new output
matcher for this purpose:
expect { my_method }.to output("my message").to_stdoutexpect { my_method }.to output("my error").to_stderr
Minitest
Minitest also has something called capture_io
:
out, err = capture_io do my_methodendassert_equals "my message", outassert_equals "my error", err
RSpec < 3.0 (and others)
For RSpec < 3.0 and other frameworks, you can use the following helper. This will allow you to capture whatever is sent to stdout and stderr, respectively:
require 'stringio'def capture_stdout(&blk) old = $stdout $stdout = fake = StringIO.new blk.call fake.stringensure $stdout = oldenddef capture_stderr(&blk) old = $stderr $stderr = fake = StringIO.new blk.call fake.stringensure $stderr = oldend
Now, when you have a method that should print something to the console
def my_method # ... print "my message"end
you can write a spec like this:
it 'should print "my message"' do printed = capture_stdout do my_method # do your actual method call end printed.should eq("my message")end
If your goal is only to be able to test this method, I would do it like this:
class Executable def initialize(outstream, instream, file) @outstream, @instream, @file = outstream, instream, file end def prompt_create_file @outstream.print "'#{@file}' doesn't exist: Create Empty File (y/n)?" endend# when executing for real, you would do something like# Executable.new $stdout, $stdin, ARGV[0]# when testing, you would dodescribe 'Executable' do before { @input = '' } let(:instream) { StringIO.new @input } let(:outstream) { StringIO.new } let(:filename) { File.expand_path '../testfile', __FILE__ } let(:executable) { Executable.new outstream, instream, filename } specify 'prompt_create_file prompts the user to create a new file' do executable.prompt_create_file outstream.string.should include "Create Empty File (y/n)" endend
However, I want to point out that I would not test a method like this directly. Instead, I'd test the code that uses it. I was talking with a potential apprentice yesterday, and he was doing something very similar, so I sat down with him, and we reimplemented a portion of the class, you can see that here.
I also have a blog that talks about this kind of thing.