pytest框架内指定用例的执行顺序

162 阅读3分钟

方法 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 的内部结构有一定了解
动态参数化适合动态数据,可以与外部数据集成数据处理逻辑较复杂
测试计划文件控制顺序易于维护,可由非开发人员管理依赖外部文件,复杂项目需额外验证文件内容