方法 1:使用 pytest_collection_modifyitems 钩子
在 pytest 中,可以通过实现 pytest_collection_modifyitems 钩子函数来重新排序测试用例。
示例代码
python
import pytest
def pytest_collection_modifyitems(items):
"""
自定义排序逻辑
:param items: pytest 收集到的测试用例列表
"""
# 定义一个排序函数,优先按照自定义 order 值排序
def get_order(item):
# 提取自定义的 order 标记,默认值为 100
marker = item.get_closest_marker("run")
if marker:
return marker.kwargs.get("order", 100)
return 100 # 未定义 order 时的默认值
# 对测试用例列表进行排序
items.sort(key=get_order)
# 测试用例
@pytest.mark.run(order=2)
def test_case_2():
print("Test case 2 executed")
@pytest.mark.run(order=1)
def test_case_1():
print("Test case 1 executed")
@pytest.mark.run(order=3)
def test_case_3():
print("Test case 3 executed")
输出结果
运行时,用例将按自定义的 order 值从小到大排序:
plaintext
Test case 1 executed
Test case 2 executed
Test case 3 executed
pytest_generate_tests 是一个 全局级别 的钩子函数,它不能定义在类内,而是需要在模块级别(文件的顶层)定义,如果使用模版动态生成测试类的,需要添加函数到测试类上方方可生效。
方法 2:通过自定义参数动态控制顺序
如果 order 是动态生成的,可以使用 pytest 的 metafunc 钩子函数。
示例代码
python
import pytest
# 动态生成测试数据,包括 order 和其他数据
test_cases = [
{"order": 2, "data": "test_case_2_data"},
{"order": 1, "data": "test_case_1_data"},
{"order": 3, "data": "test_case_3_data"},
]
def pytest_generate_tests(metafunc):
"""
动态生成测试用例及顺序
"""
if "test_input" in metafunc.fixturenames:
# 按照 order 值排序测试数据
sorted_cases = sorted(test_cases, key=lambda x: x["order"])
metafunc.parametrize("test_input", sorted_cases)
# 测试用例
def test_case(test_input):
"""
使用动态数据进行测试
"""
print(f"Executing test case with data: {test_input}")
输出结果
运行时,用例按照 order 的值动态排序并执行:
plaintext
Executing test case with data: {'order': 1, 'data': 'test_case_1_data'}
Executing test case with data: {'order': 2, 'data': 'test_case_2_data'}
Executing test case with data: {'order': 3, 'data': 'test_case_3_data'}
方法 3:通过测试计划文件控制顺序
可以在外部定义一个 JSON 或 YAML 测试计划文件,加载该文件并按照顺序执行。
测试计划文件 (test_plan.json)
json
[ {"order": 1, "case": "test_case_1"}, {"order": 3, "case": "test_case_3"}, {"order": 2, "case": "test_case_2"}]
测试代码
import pytest
import json
# 加载测试计划
with open("test_plan.json") as f:
test_plan = json.load(f)
@pytest.mark.parametrize("test_case", test_plan, ids=lambda x: x["case"])
def test_dynamic_order(test_case):
print(f"Executing: {test_case['case']} with order {test_case['order']}")
输出结果
根据测试计划文件中的顺序执行:
plaintext
Executing: test_case_1 with order 1
Executing: test_case_2 with order 2
Executing: test_case_3 with order 3
方案对比
| 方法 | 优点 | 缺点 |
|---|---|---|
| 钩子函数排序 | 灵活,直接内置到 pytest 流程 | 需要对 pytest 的内部结构有一定了解 |
| 动态参数化 | 适合动态数据,可以与外部数据集成 | 数据处理逻辑较复杂 |
| 测试计划文件控制顺序 | 易于维护,可由非开发人员管理 | 依赖外部文件,复杂项目需额外验证文件内容 |