24

How can I patch and mock getLogger in this module under test (MUT):

# Start of the module under test
import logging
log = logging.getLogger('some_logger')
# ...

I would like to do:

mock_logging.getLogger.return_value = Mock()

However I can't create mock_logging before importing the MUT, but importing the MUT already calls getLogger...

Jonathan Livni
  • 101,334
  • 104
  • 266
  • 359

3 Answers3

16

This can be done by first importing and patching logging.getLogger and only then import your mut

import unittest
from unittest import mock

import logging
with mock.patch('logging.getLogger') as mock_method:
    import mut
mock_method.assert_called_once_with('some_logger')

Alternatively, useful when patching more than one method:

import unittest
from unittest import mock

import logging
mock_method = mock.patch('logging.getLogger').start()
import mut
mock_method.stop()
mock_method.assert_called_once_with('some_logger')
Pelle
  • 1,222
  • 13
  • 18
  • Is it really OK to use the mock_method object outside the with statement? – Marcus Ahlberg May 27 '19 at 12:41
  • 1
    Yes, I think it is: the context manager's `__exit__` only decouples `mock_method` from `mut.logger.getLogger` With statements do not create a scope so, the `mock_method` is still in (global) scope and available for assertions – Pelle May 27 '19 at 14:20
4

You need to import the module inside a mocked context and remove the module from sys.modules after importing so the mock doesn't persist in other tests that import mut:

import sys
from unittest import mock

with mock.patch("logging.getLogger") as mock_logging:
    import mut
    del sys.modules["mut"]
...

Alternatively with pytest:

import sys
import pytest

def mock_get_logger(name):
    print("mock logger name", name)

with pytest.MonkeyPatch().context() as ctx:
    ctx.setattr("", mock_get_logger)
    from main import app
    del sys.modules["main"]
...
Tzane
  • 2,752
  • 1
  • 10
  • 21
4

This would require importing the module without executing it first, which unfortunately for you won't work unless you do some fancy hacks like modifying the parse tree for the module, but you probably don't want to do that either.

What you could do is to modify these import-time reference after the import and replace them manually by mock-objects or reexecute the statements after your mock-object is install, though I know, that this is of limited use.

rumpel
  • 7,870
  • 2
  • 38
  • 39