Search code examples
pythonloggingpytestmonkeypatchingstringio

monkeypatch logging basicConfig with pytest


I have a function that that involves setting up a logger:

app/src/app/pipelines.py:

import logging

def get_log_handlers(filepath):
    return [logging.FileHandler(filepath, 
                                'w'),
            logging.StreamHandler()
            ]

def function(results_dir):
    log_handlers= get_log_handlers(f'{results_dir}\\log.txt')
    logging.basicConfig(
                    level=logging.INFO,
                    format='%(asctime)s %(levelname)s:  %(message)s',
                    handlers=log_handlers
                )
    
    logger = logging.getLogger()
    logger.info('---PARAMETERS---')

I would like to test that the logging is going as expected using pytest, but don't want to write to a file, so in my test file I call function and monkeypatch a handler generation function so that a FileHandler is omitted for the test:

app/tests/test_pipelines.py:

from io import StringIO
import logging

def test_function(monkeypatch):
    def mock_get_log_handlers(path):
        nonlocal test_stream
        return [logging.StreamHandler(test_stream)]
    test_stream = StringIO()
    monkeypatch.setattr('app.pipelines.get_log_handlers', mock_get_log_handlers)
    function1('result_dir')
    test_stream.seek(0)
    content = test_stream.readlines()
    assert content[0] == '---PARAMETERS---'

My mock is getting called, but nothing is getting written to the test_stream (AssertionError: assert [] == ['---PARAMETERS---']).

What am I doing wrong?


Solution

  • I wasn't able to get my original solution working, but for anyone else with this issue, it seems like you should use the pytest caplog fixture. My working code looked like:

    def test_function(monkeypatch, caplog):
        #remove the FileHandler that is returned by get_log_handlers()
        def mock_get_log_handlers(path):
            return [logging.StreamHandler()]
        monkeypatch.setattr('app.pipelines.get_log_handlers', mock_get_log_handlers)
        #note logging level is specific to my basicConfig
        with caplog.at_level(logging.INFO):
            function1('result_dir')
            assert caplog.messages == ['---PARAMETERS---']