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 setUp
s, 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: