pytest mocker fixture mock模块的定义位置,而不是使用位置

2024-03-28 15:54:22 发布

您现在位置:Python中文网/ 问答频道 /正文

我在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

因为我像导入from src.utils.helper import func_a一样导入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

Tags: 文件函数frompytestsrchelpernode
1条回答
网友
1楼 · 发布于 2024-03-28 15:54:22

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

节点_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

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

测试节点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

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

测试节点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'
        ...

相关问题 更多 >