59

I have this code that I want to test:

log = logging.getLogger(__name__)


class A(object):
    def __init__(self):
        log.debug('Init')

but I cannot figure out how to assert that log.debug was called with 'Init'

I tried patching logger but inspecting it I only found a getLogger mock.

I'm sure its simple, but I just cant figure it!

Thanks in advance for any and all help!

Philip Ridout
  • 887
  • 1
  • 8
  • 14

5 Answers5

81

You can use patch.object() on the actual logging object. that lets you verify that you're using the correct logger too:

logger = logging.getLogger('path.to.module.under.test')
with mock.patch.object(logger, 'debug') as mock_debug:
    run_code_under_test()
    mock_debug.assert_called_once_with('Init')

Alternatively, if you're using Pytest, then it already has a fixture that captures logs for you:

def test_bar(caplog):
    with caplog.at_level(logging.DEBUG):
        run_code_under_test()
    assert "Init" in caplog.text
    # or, if you really need to check the log-level
    assert caplog.records[-1].message == "Init"
    assert caplog.records[-1].levelname == "DEBUG"

More info in the pytest docs on logging

hwjp
  • 15,359
  • 7
  • 71
  • 70
  • 4
    This actually works with @patch format as well. Just prefix your function with `@mock.patch.object(logging.getLogger('path.to.module.under.test'), 'debug')` and give the function the extra argument `mock_debug`. – Lazerbeak12345 May 21 '21 at 18:13
40

Assuming log is a global variable in a module mymod, you want to mock the actual instance that getLogger returned, which is what invokes debug. Then, you can check if log.debug was called with the correct argument.

with mock.patch('mymod.log') as log_mock:
    # test code
    log_mock.debug.assert_called_with('Init')
chepner
  • 497,756
  • 71
  • 530
  • 681
  • 1
    Can't believe it was so simple, just what I was looking for. Thanks for the quick reply too! – Philip Ridout Sep 19 '13 at 18:50
  • 3
    From 3.4 on, the test framework includes a self.assertLogs() method! Check it out: https://stackoverflow.com/a/34920727/2498426 – Jerther Mar 06 '19 at 14:02
5

I am late for this question but another of way to achieve it is:

@patch('package_name.module_name.log')
def test_log_in_A(self, mocked_log):

    a = A()
    mocked_log.debug.assert_called_once_with('Init')
emcas88
  • 881
  • 6
  • 15
2

This also works:

from unittest.mock import patch

class Test(TestCase):
    def test_logger(self):
        with patch('logging.Logger.warning') as mocked_logger:
            call_func()
            mocked_logger.assert_called_once_with('log')
sankalp
  • 376
  • 3
  • 6
1

Here is a complete example

"""
Source to test
"""
import logging

logger = logging.getLogger("abc")

def my_fonction():
    logger.warning("Oops")

"""
Testing part
"""
import unittest
from unittest.mock import patch, MagicMock

abc_logger = logging.getLogger("abc")

class TestApp(unittest.TestCase):

    @patch.object(abc_logger, "warning", MagicMock())
    def test_my_fonction(self):
        # When
        my_fonction()
        # Then
        abc_logger.warning.assert_called_once()