如何模拟被测试模块直接导入的函数,而不知道python中的模块名称

如何模拟被测试模块直接导入的函数,而不知道python中的模块名称,python,mocking,python-mock,Python,Mocking,Python Mock,假设我在模块中定义了一个函数: 模块a.py 我想创建一个API来修补函数: patcher.py 问题是,我不知道将使用我的API的模块的名称。所以一个测试看起来像这样: test1.py 会有用的。但是这样写的测试: test2.py 将失败 无论如何,API用户都不能编写它的测试和模块!与第一个选项类似?有一种方法可以在不知道修补程序发生在何处的情况下修补函数。这是我的问题的要求,因为补丁程序是我的库API,我不想被赋予使用我的库的每个测试模块的路径 我找到的解决方案是传递所有已加载的模块

假设我在模块中定义了一个函数:

模块a.py

我想创建一个API来修补函数:

patcher.py

问题是,我不知道将使用我的API的模块的名称。所以一个测试看起来像这样:

test1.py

会有用的。但是这样写的测试:

test2.py

将失败


无论如何,API用户都不能编写它的测试和模块!与第一个选项类似?

有一种方法可以在不知道修补程序发生在何处的情况下修补函数。这是我的问题的要求,因为补丁程序是我的库API,我不想被赋予使用我的库的每个测试模块的路径

我找到的解决方案是传递所有已加载的模块,并尝试在其中找到foo,然后更改它——比如我自己实现补丁。如果导入只在补丁程序启动后发生,我自己加载了模块,并对其进行了更改

现在代码将如下所示:

修补程序


你不会在函数定义的地方修补它们,而是在使用它们的地方修补它们。为“未知模块”编写测试的开发人员将知道如何应用模拟补丁。为这些模块编写测试不是你的工作。你为什么要首先编写补丁程序?补丁程序是我库的API。我正在修补的库比这个示例更复杂。
def foo():
    return 10
import mock

class Patcher(object):

    def __enter__(self):
        self.patcher = mock.patch('module_a.foo',
                                  mock.Mock(return_value=15))

        self.patcher.start()

    def __exit__(self, *args):
        self.patcher.stop()
from patcher import Patcher
import module_a

with Patcher():
    assert module_a.foo() == 15
from patcher import Patcher
from module_a import foo

with Patcher():
    assert foo() == 15
import sys
import mock

from module_a import foo as _orig_foo

import module_a

class Patcher(object):

    def __init__(self):
        self.undo_set = set()
        self.fake_foo = mock.Mock(return_value=15)

    def __enter__(self):
        modules = [
            module for mod_name, module in sys.modules.items() if
            mod_name is not None and module is not None and
            hasattr(module, '__name__') and
            module.__name__ not in ('module_a', 'patcher')
        ]

        for module in modules:
          for attr in dir(module):
                try:
                    attribute_value = getattr(module, attr)
                except (ValueError, AttributeError, ImportError):
                    # For some libraries, this happen.
                    continue

                if id(attribute_value) == id(_orig_foo):
                    setattr(module, attr, self.fake_foo)
                    self.undo_set.add((module, attr, attribute_value))

        # Solve for future imports
        module_a.foo = self.fake_foo


    def __exit__(self, *args):
        module_a.foo = _orig_foo
        for mod, attr, val in self.undo_set:
            setattr(mod, attr, val)
        self.undo_set = set()