You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从PyTest插件动态创建Fixture并注入测试用例?

问题:PyTest插件动态创建Fixture无法被测试识别

需要开发PyTest插件,实现动态创建Fixture并注入现有测试用例,用于兼容遗留测试的setup/teardown钩子;要求支持module作用域的Fixture。尝试用pytest_collection_modifyitems钩子实现时,代码在模块内运行正常,但作为插件运行时,测试无法识别注入的Fixture。

最小复现代码

plugin.py

from typing import List, ModuleType
import pytest
from _pytest.python import Function
from _pytest.nodes import Item

def pytest_collection_modifyitems(items: List[Item]):
    for item in items:
        if not isinstance(item, Function):
            continue
        pytest_function: Function = item.getparent(Function)    
        def _generate_fixture(name: str):
            @pytest.fixture(name=name)
            def _fixture():
                print("Setup...")
                yield
                print("Teardown...")

            return _fixture

        def inject_fixture(name: str, module: ModuleType):
            setattr(module, name, _generate_fixture(name))

        inject_fixture("example_fixture", pytest_function.module)
        pytest_function: Function = item.getparent(Function)
        pytest_function.fixturenames.append("example_fixture")

test_dummy.py

def test_dummy_func():
    print(globals())
    pass

预期与实际行为

  • 预期:执行pytest test_dummy.py -r P时,控制台输出Setup...和Teardown...
  • 实际:测试报错找不到Fixture,错误信息:

============================ ERRORS =============================
_______________ ERROR at setup of test_dummy_func _______________
file /local/home/harleyip/workspaces/LambdaWorkerPythonTestDriver/src/LambdaWorkerPythonTestDriver/test_dummy.py, line 3
def test_dummy_func():
E fixture 'example_fixture' not found

available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory
  use 'pytest --fixtures [testpath]' for help on them.
  • 禁用Fixture依赖后,测试打印globals()显示example_fixture已存在于模块全局变量中:

----------Captured stdout call--------------
...
'example_fixture': <function pytest_collection_modifyitems..generate_fixture.._fixture at 0x7fc6336daf70>}
...

解决方法

问题根源在于pytest_collection_modifyitems钩子执行时,pytest已完成测试模块的Fixture扫描工作,此时往模块添加Fixture函数不会被识别。需在Fixture扫描阶段之前注入,或直接通过pytest API注册Fixture。

方案1:提前注入Fixture到模块

使用pytest_pycollect_makeitem钩子(测试项创建时触发,早于Fixture扫描)注入Fixture:

from typing import Optional
import pytest
from _pytest.python import Function, PyCollector
from _pytest.nodes import Item

def pytest_pycollect_makeitem(collector: PyCollector, name: str, obj):
    # 收集到测试函数时,先往模块注入Fixture
    if isinstance(obj, type(lambda: None)) and name.startswith("test_"):
        def _generate_fixture(fixture_name: str):
            @pytest.fixture(name=fixture_name, scope="module")  # 可指定作用域
            def _fixture():
                print("Setup...")
                yield
                print("Teardown...")
            return _fixture
        
        fixture_name = "example_fixture"
        setattr(collector.module, fixture_name, _generate_fixture(fixture_name))
    # 执行默认收集逻辑
    return None

def pytest_collection_modifyitems(items: list[Item]):
    for item in items:
        if isinstance(item, Function) and "example_fixture" not in item.fixturenames:
            item.fixturenames.append("example_fixture")

方案2:全局注册Fixture

通过pytest_configure钩子直接将Fixture注册到pytest的插件管理器,无需注入模块:

import pytest
from typing import List
from _pytest.nodes import Item
from _pytest.python import Function

def pytest_configure(config):
    def _example_fixture():
        print("Setup...")
        yield
        print("Teardown...")
    
    # 注册module作用域的Fixture
    config.pluginmanager.register(
        pytest.fixture(name="example_fixture", scope="module")(_example_fixture)
    )

def pytest_collection_modifyitems(items: List[Item]):
    for item in items:
        if isinstance(item, Function) and "example_fixture" not in item.fixturenames:
            item.fixturenames.append("example_fixture")

方案说明

  • 方案1保留模块级Fixture的独立性,适合需要为不同模块定制Fixture的场景
  • 方案2实现更简洁,全局注册Fixture,适合无需模块定制的通用场景
  • 两种方案均支持指定scope参数(如module、function等)

内容的提问来源于stack exchange,提问作者patha454

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 23:00:26