gpt4 book ai didi

python - pytest mocker fixture mock 模块来自定义的位置而不是使用的位置

转载 作者:行者123 更新时间:2023-12-05 01:12:29 26 4
gpt4 key购买 nike

我在 src/utils/helper.py

有一些 utils 函数

假设我在 utils/helper.py 中有一个名为 func_a 的函数,它在我的项目中的多个位置使用。

而且每次使用时都是这样导入的

from src.utils.helper import func_a

现在我想在我的测试中模拟这个 func_a

我想在 conftest.py 中创建一个 fixture ,这样我就不需要为每个测试文件一次又一次地编写模拟函数。

问题是,在我的模拟函数中,我不能这样写。

https://pypi.org/project/pytest-mock/

mocker.patch('src.utils.helper.func_a', return_value="some_value", autospec=True)

我必须为每个测试文件都这样写

mocker.patch('src.pipeline.node_1.func_a', return_value="some_value", autospec=True)

根据文档 https://docs.python.org/3/library/unittest.mock.html#where-to-patch

由于我要导入 func_a 之类的 from src.utils.helper import func_a 我必须模拟它的使用位置而不是定义位置。

但这种方法的问题是我无法在 conftest.py 的 fixture 中定义它

目录结构

├── src
│   ├── pipeline
│   │   ├── __init__.py
│   │   ├── node_1.py
│   │   ├── node_2.py
│   │   └── node_3.py
│   └── utils
│   ├── __init__.py
│   └── helper.py
└── tests
├── __init__.py
├── conftest.py
└── pipeline
├── __init__.py
├── test_node_1.py
├── test_node_2.py
└── test_node_3.py

最佳答案

好吧,正如您所写,如果您使用 from xxx import,则必须使用该补丁。您的第一个选择当然是在生产代码中使用完整模块导入:

node_1.py

import src.utils.helper

def node_1():
src.utils.helper.func_a()

我相信你知道这一点,但我还是想提一下。

如果您不想更改生产代码,则必须根据已打补丁的模块进行打补丁,如您所写。这基本上意味着您必须动态构建补丁位置。如果您对测试函数和测试函数进行了对称命名,您可以这样做:

conftest.py

@pytest.fixture
def mock_func_a(mocker, request):
node_name = request.node.name[5:] # we assume that the test function name is "test_node_1" for testing "node_1"
module_path = f'src.pipeline.{node_name}.func_a'
mocked = mocker.patch(module_path,
return_value="some_value",
autospec=True)
yield mocked

如果无法从测试本身推导出补丁路径,则必须向测试函数添加更多信息。这可能只有在你想做的不仅仅是 fixture 中的补丁时才有意义——否则你也可以直接添加一个 patch 装饰器。
您可以添加具有模块路径或模块路径的一部分作为参数的自定义标记:

test_node_1.py

@pytest.mark.node("node_1")
def test_node(mock_func_a):
node_1()
mock_func_a.assert_called_once()

conftest.py

@pytest.fixture
def mock_func_a(mocker, request):
mark = next((m for m in request.node.iter_markers()
if m.name == 'node'), None) # find your custom mark
if mark is not None:
node_name = mark.args[0]
module_path = f'src.pipeline.{node_name}.func_a'
mocked = mocker.patch(module_path,
return_value="some_value",
autospec=True)
yield mocked

或者,如果您需要提供完整路径:

test_node_1.py

@pytest.mark.path("src.pipeline.node_1")
def test_node(mock_func_a):
...

conftest.py

@pytest.fixture
def mock_func_a(mocker, request):
mark = next((m for m in request.node.iter_markers()
if m.name == 'path'), None) # find your custom mark
if mark is not None:
node_name = mark.args[0]
module_path = f'{node_name}.func_a'
...

关于python - pytest mocker fixture mock 模块来自定义的位置而不是使用的位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61848315/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com