How to assert output with nosetest/unittest in python?
I use this context manager to capture output. It ultimately uses the same technique as some of the other answers by temporarily replacing sys.stdout
. I prefer the context manager because it wraps all the bookkeeping into a single function, so I don't have to re-write any try-finally code, and I don't have to write setup and teardown functions just for this.
import sysfrom contextlib import contextmanagerfrom StringIO import StringIO@contextmanagerdef captured_output(): new_out, new_err = StringIO(), StringIO() old_out, old_err = sys.stdout, sys.stderr try: sys.stdout, sys.stderr = new_out, new_err yield sys.stdout, sys.stderr finally: sys.stdout, sys.stderr = old_out, old_err
Use it like this:
with captured_output() as (out, err): foo()# This can go inside or outside the `with` blockoutput = out.getvalue().strip()self.assertEqual(output, 'hello world!')
Furthermore, since the original output state is restored upon exiting the with
block, we can set up a second capture block in the same function as the first one, which isn't possible using setup and teardown functions, and gets wordy when writing try-finally blocks manually. That ability came in handy when the goal of a test was to compare the results of two functions relative to each other rather than to some precomputed value.
If you really want to do this, you can reassign sys.stdout for the duration of the test.
def test_foo(): import sys from foomodule import foo from StringIO import StringIO saved_stdout = sys.stdout try: out = StringIO() sys.stdout = out foo() output = out.getvalue().strip() assert output == 'hello world!' finally: sys.stdout = saved_stdout
If I were writing this code, however, I would prefer to pass an optional out
parameter to the foo
function.
def foo(out=sys.stdout): out.write("hello, world!")
Then the test is much simpler:
def test_foo(): from foomodule import foo from StringIO import StringIO out = StringIO() foo(out=out) output = out.getvalue().strip() assert output == 'hello world!'
Since version 2.7, you do not need anymore to reassign sys.stdout
, this is provided through buffer
flag. Moreover, it is the default behavior of nosetest.
Here is a sample failing in non buffered context:
import sysimport unittestdef foo(): print 'hello world!'class Case(unittest.TestCase): def test_foo(self): foo() if not hasattr(sys.stdout, "getvalue"): self.fail("need to run in buffered mode") output = sys.stdout.getvalue().strip() # because stdout is an StringIO instance self.assertEquals(output,'hello world!')
You can set buffer through unit2
command line flag -b
, --buffer
or in unittest.main
options.The opposite is achieved through nosetest
flag --nocapture
.
if __name__=="__main__": assert not hasattr(sys.stdout, "getvalue") unittest.main(module=__name__, buffer=True, exit=False) #. #---------------------------------------------------------------------- #Ran 1 test in 0.000s # #OK assert not hasattr(sys.stdout, "getvalue") unittest.main(module=__name__, buffer=False) #hello world! #F #====================================================================== #FAIL: test_foo (__main__.Case) #---------------------------------------------------------------------- #Traceback (most recent call last): # File "test_stdout.py", line 15, in test_foo # self.fail("need to run in buffered mode") #AssertionError: need to run in buffered mode # #---------------------------------------------------------------------- #Ran 1 test in 0.002s # #FAILED (failures=1)