在同一模块中使用 unittest.mock 的补丁,通过“__main__.imported_obj”打补丁时得到 "does not have the attribute"

Using unittest.mock's patch in same module, getting "does not have the attribute" when patching via "__main__.imported_obj"

我有一个本来应该很简单的任务,但它让我困惑了一段时间。我正在尝试 patch 将对象导入当前模块。

根据对 Mock patching from/import statement in Python

的回答

我应该可以 patch("__main__.imported_obj")。但是,这对我不起作用。请查看我下面的最小重现(我正在 运行 通过 pytest 进行测试):

最小复制

这是 运行 使用 Python 3.8.6.

from random import random
from unittest.mock import patch

import pytest

@pytest.fixture
def foo():
    with patch("__main__.random"):
        return

def test(foo) -> None:
    pass

当我 运行 此代码使用 PyCharm 时,我得到一个 AttributeError:

AttributeError: <module '__main__' from '/Applications/PyCharm.app/Contents/plugins/python/helpers/pycharm/_jb_pytest_runner.py'> does not have the attribute 'random'

此外,当我在 with patch 之前的行中进入调试器模式时,我看到属性 __main__ 未定义。我不确定是否需要为 patch 定义它来发挥它的魔力。

注意:我知道我可以使用 patch.object,而且它变得容易多了。但是,我想弄清楚如何在这个问题中使用 patch

研究

这个问题是相关的,因为它既是类似的错误消息又是用例。他们的解决方案是使用 builtins 而不是 __main__,但那是因为他们试图 patch 内置函数 (open).

您假设测试 运行 所在的模块是 __main__,但只有通过 main 调用时才会出现这种情况。如果您使用 unittest,通常就是这种情况。使用 pytest,测试存在于它们定义的模块中。

您必须修补当前模块,其名称可通过 __name__ 访问,而不是假定特定的模块名称:

from random import random
from unittest.mock import patch

import pytest

@pytest.fixture
def foo():
    with patch(__name__ + ".random"):
        yield