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 sys
import unittest
import logging
logger = logging.getLogger()
logger.level = logging.DEBUG
stream_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 sys
import unittest
import logging
logger = logging.getLogger()
logger.level = logging.DEBUG
class 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 prefer
class 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