Mocking logging module for unittests

written by Domen Kožar, on Mar 4, 2009 6:07:00 PM.

Today I was writing tests for upcoming application I'm writing (more information will come later) and I needed some way to redirect logging to some kind of storage that could be easily asserted to values in tests. First I tried with monkey patching it, but that wasn't the solution since all logging setup is done before pylons tests come to play, so somebody in IRC pointed me toward writing a handler. I thought to do so in the first place, but were too stubborn;) Here is the handler code:
class ListHandler(logging.Handler):

    debug = []
    warning = []
    info = []
    error = []

    def emit(self, record):
        getattr(self.__class__, record.levelname.lower()).append(record.getMessage())

    @classmethod
    def reset(cls):
        for attr in dir(cls):
            if isinstance(getattr(cls, attr), list):
                setattr(cls, attr, [])
So basically, all records are stored in handlers class(!), which is restored to zero on every tearDown() call. Later in the code, I can do stuff like:
    def test_foobar(self):
        some_function_that_outputs_logging()
        self.failUnless('Invalid status' in  ListHandler.warning)

Comments

  • Great work...I was looking to do exactly what you described here and this does just what I need.

    Comment by Jamie Kirkpatrick — May 22, 2009 7:52:14 PM | # - re

  • The article is very good. Write please more

    Comment by KattyBlackyard — Jun 15, 2009 12:26:00 PM | # - re

  • Hello, I recently found myself in this situation and I liked your idea, except for the part where the class itself is modified, so I rewrote it to:
    class MockLoggingHandler(logging.Handler):
        
        def __init__(self, *args, **kwargs):
            self.reset()
            super(MockLoggingHandler, self).__init__(*args, **kwargs)
    
        def emit(self, record):
            self.messages[record.levelname.lower()].append(record.getMessage())
        
        def reset(self):
            self.messages = {
                'debug': [],
                'info': [],
                'warning': [],
                'error': [],
                'critical': [],
            }
    
    Thanks, - Gustavo.

    Comment by Gustavo — Jun 26, 2009 6:06:00 PM | # - re

  • Hey, I had something similar at the beginning but I didn't like the idea of making an instance every time I wanted to test output. Of course, using object isntance is more clean and elegant. d.

    Comment by Domen Kožar — Jun 27, 2009 1:08:35 AM | # - re

  • The cleaner the code, the easier it is to modify it and customize it.

    Comment by Albert Fang — Aug 23, 2009 8:01:58 PM | # - re

Leave a Reply