Testing logging output with pytest

Viewed 43992

I am trying to write a test, using pytest, that would check that a specific function is writing out a warning to the log when needed. For example:

In module.py:

import logging
LOGGER = logging.getLogger(__name__)

def run_function():
    if something_bad_happens:
        LOGGER.warning('Something bad happened!')

In test_module.py:

import logging
from module import run_function

LOGGER = logging.getLogger(__name__)

def test_func():
    LOGGER.info('Testing now.')
    run_function()
    ~ somehow get the stdout/log of run_function() ~
    assert 'Something bad happened!' in output

I have seen that you can supposedly get the log or the stdout/stderr with pytest by passing capsys or caplog as an argument to the test, and then using either capsus.readouterr() or caplog.records to access the output.

However, when I try those methods, I only see "Testing now.", and not "Something bad happened!". It seems like the logging output that is happening within the call to run_function() is not accessible from test_func()?

The same thing happens if I try a more direct method, such as sys.stdout.getvalue(). Which is confusing, because run_function() is writing to the terminal, so I would think that would be accessible from stdout...?

Basically, does anyone know how I can access that 'Something bad happened!' from within test_func()?

4 Answers

I don't know why this didn't work when I tried it before, but this solution works for me now:

In test_module.py:

import logging
from module import run_function

LOGGER = logging.getLogger(__name__)

def test_func(caplog):
    LOGGER.info('Testing now.')
    run_function()
    assert 'Something bad happened!' in caplog.text

test_module.py should look like this:

import logging
from module import run_function

LOGGER = logging.getLogger(__name__)

def test_func(caplog):
    with caplog.at_level(logging.WARNING):
        run_function()
    assert 'Something bad happened!' in caplog.text

or, alternatively:

import logging
from module import run_function

LOGGER = logging.getLogger(__name__)

def test_func(caplog):
    caplog.set_level(logging.WARNING)
    run_function()
    assert 'Something bad happened!' in caplog.text

Documentation for pytest capture logging is here

In your logging set up, check propagate is set to True, otherwise caplog handler is not able to see the logging message.

I also want to add to this thread for anybody in the future coming across this. You may need to use

@pytest.fixture(autouse=True)

as a decorator on your test so the test has access to the caplog fixture.

Related