In some situations it would be very nice to have some extra (external) logging output when unittest failures happen. Suppose these logs can be potentially big - you would only want them outputted if something actually failed.
The tearDown can't help you much here - you don't have access to the result. Ideally we would have a custom TestCase class that provides this functionality and subclass it whenever we need this behavior. We could override the run method like described here but unfortunately that's not very robust - for example nose will not call it (nose rewrapps all your test methods in different TestCase instances).
We could write a nose plugin but that's a bad idea ... we would have to tag all our tests with a flag that activates this special behavior and, well, we're getting locked in.
Here's a base TestCase that provides said behavior:
from logging import getLogger from unittest.case import TestCase, SkipTest logger = getLogger(__name__) class LogfileRecordingTestCase(TestCase): _log_offset = 0 log_limit = 1024*1024 # no more than 1mb log_name = 'foobar.log' def __init__(self, methodName='runTest'): meth = getattr(self, methodName) @wraps(meth) def wrapper(): try: return meth() except SkipTest: raise except: self._print_logs() raise wrapper.__name__ = wrapper.description = wrapper.__doc__ = methodName setattr(self, methodName, wrapper) super(LogfileRecordingTestCase, self).__init__(methodName) def _print_logs(self): try: if os.path.isfile(self.log_name): with open(self.log_name, 'r') as fh: fh.seek(self._log_offset) output = fh.read() if output: print("*"*80) print((" " + self.log_name + " ").center(80)) print(output[-self.log_limit:]) print("*"*80) except Exception: logger.exception("Failed to extract logfile data.") def setUp(self): super(LogfileRecordingTestCase, self).setUp() if os.path.isfile(self.log_name): with open(self.log_name, 'r') as fh: fh.seek(0, os.SEEK_END) self._log_offset = fh.tell()
- If you're running some external service that generates this logs (you probably are if you really need this) you can have buffering issues. Worth taking a look at PYTHONUNBUFFERED and unbuffer if you're doing some shell pipe redirects.
- You might want to from unittest2.case import SkipTest if you're using unittest2 - the classes will differ, and nose prefers the unittest2 import.