Sometimes, you want to assert that a method under test outputs some specific information to stdout. Or you want to silence all stdout from a library gone rogue (this happened to me once when I had to include a third-party library into a ruby C extension gem).
I wrote a Gem that does this, and MiniTest and RSpec already have matchers for this:
The drawback with the MiniTest matcher is that it doesn’t suppress output, so when testing a very talkative module, your test results might look pretty ugly.
Capturing output - first version
You might want to catch/mute output selectively not only via test matchers, but also during runtime. Here’s how to do this:
1 2 3 4 5 6 7 8 9 10
A little explanation:
The $stdout Ruby variable represents the console output stream. It’s compatible to StringIO. Any StringIO object we assign to it is used by ruby’s output methods.
StringIO#string() returns the contents of this object, in our case anything that has been written to it
In the code above, we switch stdout to our own StringIO object, call the code whose output we’re interested in and then restore the original stdout.
Note that we do the restoring in an
ensure block so that we can be sure $stdout is “repaired” even if we’re encountering an exception!
That’s a lot of clutter just for catching a string, so let’s put this into a method:
Capturing output - helper method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
This helper returns the result of the passed block and everything that has been written to stdout during execution of said block. Since a new stdout replacement is created every time the helper is called, we can be sure that we’re only dealing with the passed block’s output.
Capturing output - Gem
The OStreamCatcher Gem already implements above method:
1 2 3 4 5 6 7 8 9
Beware of STDOUT
You remember Coworker Greg, who made an art out of breaking the logfile analyzer, don’t you?
Well, let’s put a stop to that!
1 2 3 4
That’ll teach him, right? Bad news: good old Greg has some tricks of his own up his sleeve!
1 2 3 4
STDOUT is the global “original” stdout and can’t be overridden. So there’s still some potential for output escaping our stdout override, albeit restricted to the odd corner case.