在 pytest 中模拟模块导入

问题描述 投票:0回答:3

我正在编写一个 pytest 插件,该插件应该测试设计用于在一组特定环境中工作的软件。

我正在编写的软件在一个更大的框架内运行,这使得某些Python模块仅当在框架内运行我的Python软件时才可用。

为了测试我的软件,我需要“模拟”或伪造整个模块(实际上,很多)。我需要以某种类似的方式实现其功能,但我的问题是我应该如何使用 py.test 插件使这个假 Python 模块可用于我的软件代码?

例如,假设我的源文件之一中有以下代码:

import fwlib

def fw_sum(a, b):
    return fwlib.sum(a, b)

但是,

fwlib
模块仅由我运行软件的框架提供,我无法在其中进行测试。

我如何确保在 pytest 插件中,名为

fwlib
的模块已在
sys.modules
中定义?当然,我需要自己实现
fwlib.sum
。我正在寻找有关如何做到这一点的建议。

python pytest
3个回答
46
投票

pytest 为该用例提供了一个固定装置:

monkeypatch.syspath_prepend

您可以在

sys.path
导入位置列表中添加路径。编写一个假的
fwlib.py
并将其包含在您的测试中,并根据需要附加目录。与其他测试模块一样,它不需要包含在发行版中。

在自己尝试过这个之后,我实际上无法弄清楚如何从库代码中正确地模拟模块级导入。当测试运行时,库代码已经导入,然后修补就太晚了。

但是,我可以提供一种有效的不同解决方案:您可以从

conftest.py
中注入名称,首先导入。被测试代码中的后续导入语句将仅重用
sys.modules
中已存在的对象。

封装结构:

$ tree .
.
├── conftest.py
├── lib
│   └── my_lib.py
└── tests
    └── test_my_lib.py

2 directories, 3 files

文件内容:

# conftest.py
import sys

def fwlib_sum(a, b):
    return a + b

module = type(sys)('fwlib')
module.sum = fwlib_sum
sys.modules['fwlib'] = module

库文件:

# lib/my_lib.py
import fwlib

def fw_sum(a, b):
    return fwlib.sum(a, b)

测试文件:

# lib/test_my_lib.py
import my_lib

def test_sum():
    assert my_lib.fw_sum(1, 2) == 3

4
投票

只是为了向 @wim 的好答案提供更多细节,您也可以将它与子模块一起使用,如下所示:

import sys
module = type(sys)("my_module_name")
module.submodule = type(sys)("my_submodule_name")
module.submodule.something = sommething
sys.modules["my_module_name"] = module
sys.modules["my_module_name.my_submodule_name"] = module.submodule

0
投票

另外,根据@Itération的回答,我发现你也可以使用像这样的装置来避免污染所有测试:

# conftest.py
@pytest.fixture()
def mock_lib_installed():
    def lib_func():
        print("lib_func called")

    module = type(sys)("my_module_name")
    module.submodule = type(sys)("submodule")
    module.submodule.lib_func = lib_func
    sys.modules["my_module_name"] = module
    sys.modules["my_module_name.submodule"] = module.submodule

    yield

    del sys.modules["my_module_name"]
    del sys.modules["my_module_name.submodule"]
© www.soinside.com 2019 - 2024. All rights reserved.