session级pytest fixture重复实例化问题及实现方案咨询
解决pytest session级fixture重复创建的问题
问题描述
需要实现一个session级的pytest fixture,要求在测试会话启动时创建,供所有测试文件及模块的测试用例复用,会话结束时销毁(比如单次运行的串口连接)。但设置scope='session'后,每个测试文件都会创建一个my_fixture实例,而非仅创建一次。
目录结构
/test/fixture_1.py /test/test_1.py /test/test_2.py /pytest.ini
执行计划异常输出
(venv) PS C:\tmp_test_pytest> pytest -m single_source -s --setup-plan ============================ test session starts ================================ platform win32 -- Python 3.10.4, pytest-7.4.3, pluggy-1.3.0 rootdir: C:\tmp_test_pytest configfile: pytest.ini collected 3 items test\test_1.py SETUP S my_fixture test/test_1.py::test_from_other_file_src1 (fixtures used: my_fixture) test\test_2.py SETUP S my_fixture test/test_2.py::test_from_other_file_src21 (fixtures used: my_fixture) test/test_2.py::test_from_other_file_src22 (fixtures used: my_fixture) TEARDOWN S my_fixture TEARDOWN S my_fixture
原有代码
/test/fixture_1.py:
import pytest import datetime import time import sys @pytest.fixture(scope="session") def my_fixture(): stamp = datetime.datetime.now().strftime("%H:%M:%S.%f") time.sleep(5) print("huhu", file=sys.stderr) return stamp
- 测试文件
/test/test_1.py:
from test.fixture_1 import my_fixture @pytest.mark.single_source def test_from_other_file_src1(my_fixture): print(f"test_from_other_file: {my_fixture}", file=sys.stderr)
- 测试文件
/test/test_2.py:
from test.fixture_1 import my_fixture @pytest.mark.single_source def test_from_other_file_src21(my_fixture): print(f"test_from_other_file: {my_fixture}", file=sys.stderr) @pytest.mark.single_source def test_from_other_file_src22(my_fixture): print(f"test_from_other_file: {my_fixture}", file=sys.stderr)
/pytest.ini:
[pytest] markers = single_source: single source, same import path
解决方案
核心原因
手动在每个测试文件中import fixture时,pytest会将每个导入的fixture视为独立定义(即使代码完全一致),因为导入上下文不同,导致session级fixture被重复初始化。
修复步骤
- 将fixture移至
conftest.py
pytest会自动加载测试目录下的conftest.py,其中的fixture会被全局注册,无需手动import。创建/test/conftest.py并写入fixture代码:
import pytest import datetime import time import sys @pytest.fixture(scope="session") def my_fixture(): stamp = datetime.datetime.now().strftime("%H:%M:%S.%f") time.sleep(5) print("huhu", file=sys.stderr) return stamp
之后可以删除原有的/test/fixture_1.py。
- 修改测试文件,移除手动import
测试文件中直接在测试函数参数里使用my_fixture即可,无需导入:/test/test_1.py:
import pytest @pytest.mark.single_source def test_from_other_file_src1(my_fixture): print(f"test_from_other_file: {my_fixture}", file=sys.stderr)
/test/test_2.py:
import pytest @pytest.mark.single_source def test_from_other_file_src21(my_fixture): print(f"test_from_other_file: {my_fixture}", file=sys.stderr) @pytest.mark.single_source def test_from_other_file_src22(my_fixture): print(f"test_from_other_file: {my_fixture}", file=sys.stderr)
- 验证修复效果
重新执行命令:
pytest -m single_source -s --setup-plan
输出会显示仅一次SETUP和TEARDOWN:
============================ test session starts ================================ platform win32 -- Python 3.10.4, pytest-7.4.3, pluggy-1.3.0 rootdir: C:\tmp_test_pytest configfile: pytest.ini collected 3 items SETUP S my_fixture test\test_1.py test/test_1.py::test_from_other_file_src1 (fixtures used: my_fixture) test\test_2.py test/test_2.py::test_from_other_file_src21 (fixtures used: my_fixture) test/test_2.py::test_from_other_file_src22 (fixtures used: my_fixture) TEARDOWN S my_fixture
内容的提问来源于stack exchange,提问作者Cutton Eye
相关产品推荐
相关产品推荐

