Testing Logger

Logging is sometimes a part of your requirements. For example, you might want to issue a warning anytime some security event occurs. I like to test these events using a TestingLogger class that can stand in place of a real logger.

First, make your loggers parameters to your functions and classes. Globals make testing difficult.

class Example:

    def __init__(self, logger):
        self._logger = logger

    def do_something(self):
        self._logger.info("Logged something!")


def example(logger):
    logger.info("Logged something!")

Now you can use the TestingLogger during tests. If it quacks like a duck, its a duck. Let’s make a TestingLogger that quacks like a logger.


class TestingLogger:
    def __init__(self):
        self.messages = []

    def debug(self, message):
        self._log(message)

    def info(self, message):
        self._log(message)

    def warning(self, message):
        self._log(message)

    def error(self, message):
        self._log(message)

    def critical(self, message):
        self._log(message)

    def exception(self, message):
        self._log(message)

    def _log(self, message):
        self.messages.append(message)

This class implements the methods you will call 99% of the time against a logger. Instead of logging though, it will capture the messages in a messages list. You can make assertions against this list in your tests.


def test_example_function():
    logger = TestingLogger()

    example(logger)

    assert "Logged something!" == logger.messages[0]


def test_example_class():
    logger = TestingLogger()

    Example(logger).do_something()

    assert "Logged something!" == logger.messages[0]

You can expand on this idea in many ways:

If you would like to learn more about this testing style, check out the Python architecture book. It is one of the best programming books I have read in a long time.

As an Amazon Associate, I earn from qualifying purchases