Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Discard stdout / stderr of program under test, but keep unittest output

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?

like image 200
blueFast Avatar asked Jun 08 '15 17:06

blueFast


2 Answers

@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
like image 175
sobolevn Avatar answered Oct 23 '22 05:10

sobolevn


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

like image 4
Alik Avatar answered Oct 23 '22 04:10

Alik