Testing logging output with pytest

PythonUnit TestingTestingLoggingPytest

Python Problem Overview


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()?

Python Solutions


Solution 1 - Python

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

Solution 2 - Python

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

Solution 3 - Python

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

Solution 4 - Python

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionlauren.mariettaView Question on Stackoverflow
Solution 1 - Pythonlauren.mariettaView Answer on Stackoverflow
Solution 2 - PythonKrzysieqqView Answer on Stackoverflow
Solution 3 - PythonssolerView Answer on Stackoverflow
Solution 4 - PythonCEO of PizzaView Answer on Stackoverflow