Reputation: 44371
I have this sample code (test_it.py
):
import sys
def give_me_5():
print >>sys.stdout, "STDOUT"
print >>sys.stderr, "STDERR"
return 6
import unittest
class TestMe(unittest.TestCase):
def setUp(self):
pass
def test_give_me_5(self):
self.assertEqual(give_me_5(), 5)
if __name__ == '__main__':
unittest.main()
Which gives me the following output:
» python -m unittest test_it
A long annoying output message
A long annoying error message
F
======================================================================
FAIL: test_give_me_5 (__main__.TestMe)
----------------------------------------------------------------------
Traceback (most recent call last):
File "xx.py", line 17, in test_give_me_5
self.assertEqual(give_me_5(), 5)
AssertionError: 6 != 5
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (failures=1)
But because of the long, annoying messages produced by the program under test (the real program produces LOTS of output), I am unable to see the output from unittest. I would like to get rid of the stdout / stderr of the function being tested (give_me_5
), but I still want to see stdout / stderr of unittest
. I would like to get this result:
» python -m unittest test_it
F
======================================================================
FAIL: test_give_me_5 (__main__.TestMe)
----------------------------------------------------------------------
Traceback (most recent call last):
File "xx.py", line 17, in test_give_me_5
self.assertEqual(give_me_5(), 5)
AssertionError: 6 != 5
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (failures=1)
So that the output produced by the program under test (both stdout and stderr) is filtered out by unittest, but the output produced by unittest itself is kept. I do not want to modify the code being tested (no redirection in the code itself). I just want to tell unittest that all output to stdout/stderr generated by the code under test should be discarded.
Is this possible?
Upvotes: 5
Views: 1452
Reputation: 44371
For reference (and to get comments), here is the code I have ended up using (inspired in the reply by @Alik):
import sys
def give_me_5():
print >>sys.stdout, "A long annoying output message"
print >>sys.stderr, "A long annoying error message"
return 6
import unittest
def redirect(stdout_file=None, stderr_file=None):
new_stdout = open(stdout_file, 'w') if stdout_file else None
new_stderr = open(stderr_file, 'w') if stderr_file else None
if new_stdout : sys.stdout = new_stdout
if new_stderr : sys.stderr = new_stderr
def redirect_testcase(test_case):
logfile = '.'.join([test_case.__module__, test_case.__class__.__name__, 'out'])
errfile = '.'.join([test_case.__module__, test_case.__class__.__name__, 'err'])
redirect(logfile, errfile)
class TestMe(unittest.TestCase):
def setUp(self):
redirect_testcase(self)
def test_give_me_5(self):
self.assertEqual(give_me_5(), 5)
if __name__ == '__main__':
unittest.main()
Now simply by doing a redirect_testcase(self)
, stdout / stderr get redirected to test_it.TestMe.out
/ test_it.TestMe.err
, and the output from unittest is visible in the console stdout / stderr (and can be redirected via shell if needed).
There is a problem (that I do not yet know how to fix): all tests in a specific TestCase will overwrite the .out
/ .err
files. It would be good to open a different out / log file for each test (as opposed to a common one for each TestCase)
Upvotes: 0
Reputation: 18080
@Alik suggestion was right. But I guess it could be improved.
import sys
# StringIO is replaced in Python 3:
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
class ReplaceStd(object):
""" Let's make it pythonic. """
def __init__(self):
self.stdout = None
self.stderr = None
def __enter__(self):
self.stdout = sys.stdout
self.stderr = sys.stderr
# as it was suggseted already:
sys.stdout = StringIO()
sys.stderr = StringIO()
def __exit__(self, type, value, traceback):
sys.stdout = self.stdout
sys.stderr = self.stderr
print('I am here')
with ReplaceStd():
print('I am not')
print('I am back')
And the output:
I am here
I am back
Upvotes: 6
Reputation: 25339
Temporarily replace sys.stdout
and sys.stderr
with file-like instances. For example, you can use StringIO
aka memory buffers.
from StringIO import StringIO
.....
class TestMe(unittest.TestCase):
def setUp(self):
pass
def test_give_me_5(self):
stdout = sys.stdout
stderr = sys.stderr
sys.stdout = StringIO()
sys.stderr = StringIO()
self.assertEqual(give_me_5(), 5)
sys.stdout = stdout
sys.stderr = stderr
You might want to add exception handling or even turn this code in a context manager to reuse it
Upvotes: 4