I was testing a program that took N lines of input, manipulated them somehow and returned M lines of output. To test it properly I wanted to test both the internal interface and the external interface.
The main function looked something like this:
import sys def main(): for line in sys.stdin: print do_something(line)
The internal interface, the do_something
function was easy to test.
The test just called it with an example line of input and compared the
output with expected string.
Testing the external interface was a bit harder, because I had to capture
sys.stdout
and provide my IO object for sys.stdin
. One way to do it is
to override setUp
and tearDown
methods, but I didn't want to capture
standard IO streams for all the test cases in the class. Instead I used
contextmanager
and it made my tests look beautiful. Here's the test code:
from StringIO import StringIO def test_main(): input = StringIO('foo') with provided_stdin(input), captured_stdout() as s: main() assert s.getvalue().strip() == 'bar'
What are provided_stdin
and captured_stdout
functions? They are just
generators decorated by contextmanager
. Here's the code:
from StringIO import StringIO from contextlib import contextmanager import sys @contextmanager def captured_stdout(): """ Replaces ``sys.stdout`` with StringIO object yielded to the user. """ orig_stdout = sys.stdout buffer = StringIO() try: sys.stdout = buffer yield buffer finally: sys.stdout = orig_stdout @contextmanager def provided_stdin(io): """ Replaces ``sys.stdin`` with StringIO object provided by the user. """ orig_stdin = sys.stdin try: sys.stdin = io yield finally: sys.stdin = orig_stdin
Both functions are really super simple thanks to the abstractions provided by
contextmanater
.
tl;dr contextmanager
is awesome, especially for writing test helper
functions.