stdout output tests
Wednesday, April 1st, 2009I was working on simple_example a few days ago and needed to test the output’s formatting.
The first step is to capture the output so that we can compare it later. A simple way to do this is to swap $stdout for a StringIO object. ($stdout is an IO object. Kernel#puts and Kernel#print are wrappers around $stdout.puts and $stdout.write)
def keep_stdout(&block)
begin
orig_stream, $stdout = $stdout, StringIO.new
block.call($stdout)
ensure
s, $stdout = $stdout.string, orig_stream
s
end
end
Clearly we want to restore the stdout stream after we are done with our call so that we can get out test restults printed.
Next, let’s create an assertion that wraps our stdout capture method.
def assert_output(expected, &block)
keep_stdout do |stdout|
block.call
if expected.is_a?(Regexp)
assert_match expected, stdout.string
else
stdout.string.should be(expected.to_s)
assert_equal expected.to_s, stdout.string
end
end
end
The regexp matching is so we can assert that the output contains what we’re looking for, as opposed to being exactly equal to it.
Now we can do:
assert_output('abc') { puts "abc" } #=> true
assert_output(/abc/) { puts "abc\ndef" } #=> true
If you have slightly more complex strings that you want to partially match, remember that you have to escape them first as they might contain special regexp chars. This can be achieved with a small string extention:
class String
def to_regexp
Regexp.new(Regexp.escape(self))
end
end
#to_regexp is probably a bad name, but it’s only a test helper so it’ll work for now.
This allows:
assert_output("(1 + 1)".to_regexp) do
puts "(2 + 2)\n(1 + 1)\nabc"
end
#=> true
To test for string formatting, though, I needed the expected string to be more complex than ‘abc’.
def test_formatting
out = <<-STR.unindent
(1 + 1)
#=> 2
(2 + 2)
#=> 4
STR
# ...
end
This uses the small unindent gem to allow us to keep normal indentation; otherwise we’d have to write:
def test_formatting
out = <<-STR
(1 + 1)
#=> 2
(2 + 2)
#=> 4
STR
# ...
end
Which works fine, of course. Just not as fun.
Putting it all together
require 'test/unit'
require 'rubygems'
require 'unindent'
class OutputTest < Test::Unit::TestCase
def test_formatting
out = <<-STR.unindent
(1 + 1)
#=> 2
(2 + 2)
#=> 4
----------
(3 + 3)
#=> 6
----------
STR
assert_output(out) do
@obj.first_method
@obj.other_method
end
end
end
You can see real life usage in simple_example’s test file.