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

如何在Python/Pytest中实现动态参数化,减少无效测试用例?

铁路物流系统测试用例参数化优化方案

问题背景

我正在为铁路物流系统编写测试用例,需传入多个JSON文件作为测试参数,但每个JSON对应不同测试场景(如放行载重/空车厢列车、检查站点车厢供应一致性等)。当前所有JSON会传入所有测试,导致大量无效测试用例被跳过,增加了不必要的复杂度。

当前测试代码示例

NODES_LIST = ['ECA', 'ECE', 'EDV', 'EYU', 'VEB', 'VIC', 'VOB', 'VTU']
SKIP_JSON = 'The test case file is not for the offer empty test.'
SKIP_NODE = 'The test case file is for the offer empty test, but does not apply to this station.'

@pytest.mark.parametrize('node_name', NODES_LIST)
def test_offer_empty_consistency(input_json_filename: str, node_name: str):
    if ('offer_empty' not in input_json_filename):
        pytest.skip(SKIP_JSON)
    elif (((node_name in ['ECA', 'ECE', 'EDV', 'EYU', 'VEB', 'VOB']) and ('generic' not in input_json_filename)) or ((node_name in ['VIC', 'VTU']) and (node_name not in input_json_filename))):
        pytest.skip(SKIP_NODE)

    expected_offer_empty, really_offer_empty = offer_empty_consistency(input_json_filename=input_json_filename, node_name=node_name)

    assert expected_offer_empty == really_offer_empty, f'Failure.\nExpected empty offer values [{expected_offer_empty}] differ from actual values [{really_offer_empty}] for station {node_name}.'

期望的有效测试组合

以传入3个JSON文件['CT_01_offer_empty_generic', 'CT_02_offer_empty_VIC', 'CT_03_offer_empty_VTU']为例,仅需生成以下有效测试:

test_offer_empty_consistency[CT_01_offer_empty_generic-ECA]
test_offer_empty_consistency[CT_01_offer_empty_generic-ECE]
test_offer_empty_consistency[CT_01_offer_empty_generic-EDV]
test_offer_empty_consistency[CT_01_offer_empty_generic-EYU]
test_offer_empty_consistency[CT_01_offer_empty_generic-VEB]
test_offer_empty_consistency[CT_01_offer_empty_generic-VOB]

test_offer_empty_consistency[CT_02_offer_empty_VIC-VIC]

test_offer_empty_consistency[CT_03_offer_empty_VTU-VTU]

尝试过的方案(存在问题)

曾尝试用类实现动态参数化,但node_name的参数化无法访问类实例的self变量:

@pytest.mark.parametrize('input_json_filename', test_related_lists.OFFER_EMPTY)
class TestOfferEmpty:
    def __init__(self, input_json_filename) -> None:
        self.test_data = get_test_data(input_json_filename=input_json_filename)
        if 'generic' in input_json_filename:
            self.nodes = ['ECA', 'ECE', 'EDV', 'EYU', 'VEB', 'VOB']
        elif 'VIC' in input_json_filename:
            self.nodes = ['VIC']
        else:
            self.nodes = ['VTU']

    @pytest.mark.parametrize('node_name', self.nodes)
    def test_offer_empty_consistency(self, node_name: str):
        expected_offer_empty, really_offer_empty = offer_empty_consistency(test_data=self.test_data, node_name=node_name)

        assert expected_offer_empty == really_offer_empty, f'Failure.\nExpected empty offer values [{expected_offer_empty}] differ from actual values [{really_offer_empty}] for station {node_name}.'

当前JSON文件通过conftest.py参数化传入,需实现仅传递相关JSON给对应测试,无需大量验证和跳过逻辑。


解决方案:自定义参数生成逻辑(基于pytest_generate_tests)

核心思路是在pytest_generate_tests中根据测试函数特性和JSON命名规则,提前生成仅包含有效组合的测试参数,而非先生成全量组合再跳过无效项。

步骤1:修改conftest.py的参数生成逻辑

更新conftest.py,针对不同测试函数生成对应有效参数组合:

import pytest
from science_vli_qa.tests.utils import get_input_files_list

def pytest_addoption(parser):
    parser.addoption("--input_json_filename", action="store", default=None)

def get_valid_node_json_pairs(test_func_name, json_files):
    valid_pairs = []
    # 针对空车厢供应一致性测试的规则
    if test_func_name == "test_offer_empty_consistency":
        for json_file in json_files:
            if "offer_empty" not in json_file:
                continue  # 跳过非对应场景的JSON
            # 根据JSON文件名匹配对应节点
            if "generic" in json_file:
                nodes = ['ECA', 'ECE', 'EDV', 'EYU', 'VEB', 'VOB']
            elif "VIC" in json_file:
                nodes = ['VIC']
            elif "VTU" in json_file:
                nodes = ['VTU']
            else:
                continue
            # 生成所有有效(json, node)组合,并设置测试ID
            for node in nodes:
                valid_pairs.append(pytest.param(json_file, node, id=f"{json_file}-{node}"))
    # 可扩展其他测试函数的参数规则
    return valid_pairs

def pytest_generate_tests(metafunc):
    # 获取指定或全量JSON文件列表
    if option_value := metafunc.config.option.input_json_filename:
        params = option_value.split(",")
        json_files = []
        for ct in params:
            formatted_ct_name = ct if ct.endswith('.json') else f'{ct}.json'
            json_files.append(formatted_ct_name)
    else:
        json_files = get_input_files_list()

    # 根据测试函数生成对应参数
    test_func_name = metafunc.function.__name__
    if test_func_name == "test_offer_empty_consistency":
        # 该测试需要两个参数:input_json_filename和node_name
        valid_pairs = get_valid_node_json_pairs(test_func_name, json_files)
        metafunc.parametrize("input_json_filename, node_name", valid_pairs)
    elif 'input_json_filename' in metafunc.fixturenames:
        # 其他仅需单个JSON参数的测试,沿用原有逻辑
        metafunc.parametrize("input_json_filename", json_files)

步骤2:简化测试函数代码

移除所有跳过逻辑,仅保留核心测试逻辑:

def test_offer_empty_consistency(input_json_filename: str, node_name: str):
    expected_offer_empty, really_offer_empty = offer_empty_consistency(
        input_json_filename=input_json_filename, 
        node_name=node_name
    )

    assert expected_offer_empty == really_offer_empty, (
        f'Failure.\nExpected empty offer values [{expected_offer_empty}] differ from actual values [{really_offer_empty}] for station {node_name}.'
    )

方案优势

  1. 无无效测试用例:参数生成阶段直接过滤无效组合,避免大量跳过操作
  2. 规则集中管理:所有JSON与测试、节点的映射规则集中维护,便于后续扩展其他测试场景
  3. 保留灵活性:依然支持通过--input_json_filename指定特定JSON文件,自动生成对应有效节点组合

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 11:27:57