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 sys
from contextlib import contextmanager
from StringIO import StringIO
@contextmanager
def 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` block
output = 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 sys
import unittest
def 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)
A lot of these answers failed for me because you can't from StringIO import StringIO
in Python 3. Here's a minimum working snippet based on @naxa's comment and the Python Cookbook.
from io import StringIO
from unittest.mock import patch
with patch('sys.stdout', new=StringIO()) as fakeOutput:
print('hello world')
self.assertEqual(fakeOutput.getvalue().strip(), 'hello world')
In python 3.5 you can use contextlib.redirect_stdout()
and StringIO()
. Here's the modification to your code
import contextlib
from io import StringIO
from foomodule import foo
def test_foo():
temp_stdout = StringIO()
with contextlib.redirect_stdout(temp_stdout):
foo()
output = temp_stdout.getvalue().strip()
assert output == 'hello world!'