在导入模块前进行模拟

26 投票
3 回答
17293 浏览
提问于 2025-04-17 01:20

我该如何在这个被测试的模块(MUT)中对 getLogger 进行 补丁和模拟 呢:

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

我想这样做:

mock_logging.getLogger.return_value = Mock()

但是我无法在导入MUT之前创建 mock_logging,而导入MUT时已经调用了 getLogger ...

3 个回答

4

你需要在一个模拟的环境中导入这个模块,并在导入后把这个模块从 sys.modules 中移除,这样在其他测试中导入 mut 时,模拟的效果就不会持续存在了。

import sys
from unittest import mock

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

另外,如果你使用的是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"]
...
4

这就需要在不先执行模块的情况下导入它,但很遗憾,这种方法是行不通的,除非你做一些复杂的操作,比如修改模块的解析树,但你可能也不想这么做。

你可以做的是在导入后修改这些导入时的引用,然后手动用模拟对象替换它们,或者在安装了模拟对象后重新执行那些语句,虽然我知道,这样做的用处有限。

17

这可以通过先导入并修改 logging.getLogger 来实现,然后再导入你的 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')

另外,当需要修改多个方法时,这种做法会很有用:

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')

撰写回答