In writing spec in Ruby, you would often meet a situation that you have to reduce outputting messages to STDOUT.
def f
p 123
end
describe 'f' do
it 'outputs "123\n" to stdout' do
...
end
end
How do you write it? Usually I assign a stringio object to
$stdout
. I guess many people use this approach.
def f
p 123
end
# spec
require 'stringio'
results = $stdout = StringIO.new
f()
results.close_write
results.rewind
$stdout = STDOUT
p results.read #=> "123\n"
I started having an idea that IO#reopen
may be a better solution
of it. The code previously given looks obviously dirty.
Finally I figured out the new approach was also dirty.
def f
p 123
end
require 'tempfile'
results = Tempfile.new('a').path
a = STDOUT.dup
STDOUT.reopen(results, 'w')
f()
STDOUT.reopen(a)
p File.read(results)
STDOUT.reopen
doesn't accept an IO object, but only accepts a
Filename.
capture_io of minitest
Eric Hodel and Ryan Davis mentioned about capture_io
in minitest
. It uses the former approache.
def capture_io
require 'stringio'
orig_stdout, orig_stderr = $stdout, $stderr
captured_stdout, captured_stderr = StringIO.new, StringIO.new
$stdout, $stderr = captured_stdout, captured_stderr
yield
return captured_stdout.string, captured_stderr.string
ensure
$stdout = orig_stdout
$stderr = orig_stderr
end
Reference
http://www.ruby-forum.com/topic/187367
Eric Hodel++
using the rr mocking library, i do something like
ReplyDeletemock($stdout).puts.with_any_args
It's also good, but it doesn't work when the implementation part doesn't only use puts but also use p or other methods. Once I overwrote p, puts, print and write, I noticed that I shoul overwrite the stdout itself :-)
ReplyDelete