Search code examples
pythonunit-testingloggingglobalfixtures

Add log separators to all fixtures in unittests


I'm using unittest module. I need to separate setUp, setUpClass, teardown and teardownClass logs from unittests logs. Output should look something like:

**************setting things up**************
INFO: preparing database
INFO: create new users
**************end of setup****************
INFO: starting test one
INFO: ...
**************Cleaning things**************
INFO: delete users
...

I tried to override some functions in unittest.suite (_handleClassSetUp, _handleModuleFixtures, _tearDownPreviousClass) to log separators before and after they are called. As a result separators are logged even if test case does not contain setUpClass and tearDownClass. And still, there is no separator for setup and teardown.

How can it be done?


Solution

  • You could use a metaclass to achieve this functionality. All it's doing is looking for a list of function names that you provide, and then applying a decorator to those functions. The decorator handles printing entry and exit statements.

    import functools
    import unittest
    
    FUNCTIONS_TO_LOG = ('setUp', 'tearDown')
    
    
    def log_start_and_end(f):
        @functools.wraps(f)
        def wrapper(*args, **kwargs):
            print '********** start {}'.format(f.__name__)
            f(*args, **kwargs)
            print '********** end {}'.format(f.__name__)
        return wrapper
    
    
    class LoggingMeta(type):
        def __new__(cls, name, bases, namespace):
            for attr, obj in namespace.items():
                if attr in FUNCTIONS_TO_LOG:
                    namespace[attr] = log_start_and_end(obj)
            return super(LoggingMeta, cls).__new__(cls, name, bases, namespace)
    
    
    class BaseTest(unittest.TestCase):
        __metaclass__ = LoggingMeta
    
        def setUp(self):
            print 'inside setup'
    
        def tearDown(self):
            print 'inside teardown'
    
        def test_test(self):
            print 'inside test'
    
    if __name__ == '__main__':
        unittest.main()
    

    This results in output:

    ********** start setUp
    inside setup
    ********** end setUp
    inside test
    ********** start tearDown
    inside teardown
    ********** end tearDown
    .
    

    Ran 1 test in 0.000s
    
    OK