PyDev unittesting: How to capture text logged to a logging.Logger in "Captured Output" PyDev unittesting: How to capture text logged to a logging.Logger in "Captured Output" python python

PyDev unittesting: How to capture text logged to a logging.Logger in "Captured Output"


The issue is that the unittest runner replaces sys.stdout/sys.stderr before the testing starts, and the StreamHandler is still writing to the original sys.stdout.

If you assign the 'current' sys.stdout to the handler, it should work (see the code below).

import sysimport unittestimport logginglogger = logging.getLogger()logger.level = logging.DEBUGstream_handler = logging.StreamHandler(sys.stdout)logger.addHandler(stream_handler)class TestCase(unittest.TestCase):    def testSimpleMsg(self):        stream_handler.stream = sys.stdout        print("AA")        logging.getLogger().info("BB")

Although, a better approach would be adding/removing the handler during the test:

import sysimport unittestimport logginglogger = logging.getLogger()logger.level = logging.DEBUGclass TestCase(unittest.TestCase):    def testSimpleMsg(self):        stream_handler = logging.StreamHandler(sys.stdout)        logger.addHandler(stream_handler)        try:            print("AA")            logging.getLogger().info("BB")        finally:            logger.removeHandler(stream_handler)


I grew tired of having to manually add Fabio's great code to all setUps, so I subclassed unittest.TestCase with some __metaclass__ing:

class LoggedTestCase(unittest.TestCase):    __metaclass__ = LogThisTestCase    logger = logging.getLogger("unittestLogger")    logger.setLevel(logging.DEBUG) # or whatever you preferclass LogThisTestCase(type):    def __new__(cls, name, bases, dct):        # if the TestCase already provides setUp, wrap it        if 'setUp' in dct:            setUp = dct['setUp']        else:            setUp = lambda self: None            print "creating setUp..."        def wrappedSetUp(self):            # for hdlr in self.logger.handlers:            #    self.logger.removeHandler(hdlr)            self.hdlr = logging.StreamHandler(sys.stdout)            self.logger.addHandler(self.hdlr)            setUp(self)        dct['setUp'] = wrappedSetUp        # same for tearDown        if 'tearDown' in dct:            tearDown = dct['tearDown']        else:            tearDown = lambda self: None        def wrappedTearDown(self):            tearDown(self)            self.logger.removeHandler(self.hdlr)        dct['tearDown'] = wrappedTearDown        # return the class instance with the replaced setUp/tearDown        return type.__new__(cls, name, bases, dct)

Now your test case can simply inherit from LoggedTestCase, i.e. class TestCase(LoggedTestCase) instead of class TestCase(unittest.TestCase) and you're done. Alternatively, you can add the __metaclass__ line and define the logger either in the test or a slightly modified LogThisTestCase.


I'd suggest using a LogCapture and testing that you really are logging what you expect to be logging:

http://testfixtures.readthedocs.org/en/latest/logging.html