如何使pytest.main重复执行test_init与test_deinit测试用例?
pytest重复执行初始化/清理用例被跳过的解决思路
问题背景
我需要按如下方式调用pytest执行测试:
tests = [ 'path\\testfile.py::test_init', 'path\\testfile.py::test1', 'path\\testfile.py::test2', 'path\\testfile.py::test_deinit', 'path\\testfile.py::test_init', 'path\\testfile.py::test3', 'path\\testfile.py::test4', 'path\\testfile.py::test_deinit' ] pytest.main(tests)
但pytest会跳过列表中重复的test_init和test_deinit,实际只执行6个用例,导致test3、test4没有对应的初始化与硬件清理操作。每组测试(test1/test2、test3/test4)必须单独执行test_init前置初始化和test_deinit后置清理,需要让8个用例完全按列表顺序执行。
现有测试结构
testfile.py中的测试套件定义如下:
child1 = { 'name':'child1_suite_name', 'tests':[test1, test2] } child2 = { 'name':'child2_suite_name', 'tests':[test3, test4] } testsuite = { 'name': 'test suite', 'setup': test_init, 'teardown': test_deinit, 'tests' :[child1, child2] }
现有框架可解析该字典并调用对应测试、setup与teardown,但我需要通过pytest来执行。
已尝试的无效方案
- 使用fixture:需要在测试用例参数中显式引入,不符合当前调用方式的需求
- 使用setup_module:仅在文件所有测试前后执行一次,无法实现每组测试单独初始化/清理
解决思路
思路1:动态生成唯一命名的初始化/清理用例
pytest跳过重复用例是因为识别到同一个函数引用,因此可以给每组测试的初始化/清理生成唯一命名的函数副本:
# testfile.py def test_init(): # 原初始化逻辑(硬件控制相关) pass def test_deinit(): # 原清理逻辑(硬件控制相关) pass # 为每组测试生成唯一命名的初始化/清理用例 test_init_group1 = test_init test_deinit_group1 = test_deinit test_init_group2 = test_init test_deinit_group2 = test_deinit # 原测试用例test1、test2、test3、test4保持不变
修改调用列表为:
tests = [ 'path\\testfile.py::test_init_group1', 'path\\testfile.py::test1', 'path\\testfile.py::test2', 'path\\testfile.py::test_deinit_group1', 'path\\testfile.py::test_init_group2', 'path\\testfile.py::test3', 'path\\testfile.py::test4', 'path\\testfile.py::test_deinit_group2' ] pytest.main(tests)
这样每个初始化/清理用例都有唯一标识,pytest会按顺序执行全部8个用例。
思路2:利用pytest钩子自定义测试集合与执行顺序
编写conftest.py,通过pytest_collection_modifyitems钩子重新组织测试用例的执行顺序,强制每组测试绑定对应的初始化/清理:
# conftest.py def pytest_collection_modifyitems(items): # 定义两组测试的执行序列 test_groups = [ ['test_init', 'test1', 'test2', 'test_deinit'], ['test_init', 'test3', 'test4', 'test_deinit'] ] reordered_items = [] for group in test_groups: for case_name in group: # 找到对应测试项并加入新列表,移除原列表中的项避免重复 for item in items[:]: if item.name == case_name: reordered_items.append(item) items.remove(item) # 替换原测试集合为重新排序后的集合 items[:] = reordered_items
注意:此方法需要确保pytest不对重复用例去重,若仍有跳过问题,可以通过复制测试项实例的方式解决(比如使用copy.deepcopy(item))。
思路3:直接复用现有框架的套件执行逻辑
现有框架已经支持解析测试套件字典并为每个子套件执行setup/teardown,无需转成pytest用例列表,直接调用框架的执行入口即可:
# 假设现有框架提供run_suite方法执行测试套件 from your_framework import run_suite run_suite(testsuite)
这种方式完全符合每组测试单独初始化/清理的需求,无需依赖pytest的用例执行逻辑。
内容的提问来源于stack exchange,提问作者Seafood
相关产品推荐
相关产品推荐

