3. 测试用例自动化设计总体流程

测试用例自动化设计不是“把手工用例录进脚本”这么简单,它本质上是一条从需求语义到可执行判定结果的工程流水线。整条流水线可以概括为六个环节:需求分析 → 测试点提取 → 用例建模 → 自动生成 → 执行调度 → 结果判定与报告闭环。前三个环节解决“测什么、怎么表达”,后三个环节解决“怎么跑、怎么判、怎么反馈”。任何一个环节的输入输出定义不清,都会在后续环节被放大成大量无效用例或误判。

下面这张图给出六个环节的输入输出关系与闭环路径,注意“结果判定”并不是终点,它会把失败用例反向映射回测试点,形成需求覆盖率的再评估。

① 需求分析 输入:需求/接口文档 输出:功能边界清单 ② 测试点提取 输入:功能边界清单 输出:可测点+判定条件 ③ 用例建模 输入:可测点+判定条件 输出:参数化用例模型 ④ 自动生成 输入:用例模型 输出:可执行用例集 ⑤ 执行调度 输入:可执行用例集 输出:仿真/测试结果 ⑥ 结果判定与报告 输入:仿真/测试结果 输出:通过/失败+覆盖率 失败用例反向映射测试点,触发需求覆盖再评估 各环节关键交付物 需求分析:功能边界、接口清单、约束条件 测试点提取:可测点编号、判定条件、优先级 用例建模:参数化模型、信号映射、预期输出 自动生成:用例集、脚本、数据文件 执行调度:批处理、并行、资源分配 结果判定:通过/失败、覆盖率、缺陷报告

需求分析:把自然语言需求转成可测边界

需求分析的目标不是“读懂需求”,而是把需求拆成可测的功能边界。以某电机控制器转速闭环为例,需求文档写“转速指令阶跃后,实际转速应在 200 ms 内进入 ±2% 稳态误差带”。这句话里至少包含四个可测要素:激励形式(阶跃)、激励幅值(例如 0→3000 rpm)、时间指标(200 ms)、误差带(±2%)。需求分析阶段就要把这些要素逐条列出,并标注哪些是输入、哪些是判定条件、哪些是约束(如母线电压 48 V、负载转矩 5 N·m)。

实际做法是建立一张“需求—边界”对照表,每条需求至少对应一行,字段包括需求编号、功能描述、输入变量、输出变量、判定条件、约束条件。如果某条需求无法拆出可测的输入输出,说明它要么是设计约束,要么需要向需求方澄清,不能直接进入测试点提取。

测试点提取:从边界到可测点

测试点提取是把功能边界进一步细化为“一个测试点对应一个可独立判定的行为”。仍以转速闭环为例,可提取的测试点包括:上升时间、超调量、稳态误差、抗负载扰动恢复时间、指令突变时的电流限幅行为。每个测试点要有唯一编号,例如 TP-SPD-001 对应上升时间,TP-SPD-002 对应超调量。

提取时要注意区分“正常测试点”和“边界/异常测试点”。例如转速指令从 0 跳到 3000 rpm 是正常点,而指令从 3000 rpm 跳到 -3000 rpm 属于反向大阶跃,可能触发电流限幅,应单独作为一个测试点。测试点提取的输出应是一张可测点清单,包含编号、描述、判定条件、优先级、关联需求编号。

用例建模:参数化表达测试意图

用例建模的核心是“用参数表达变化,用模型表达结构”。在 Simulink 环境下,通常把被测对象、激励源、判定逻辑放在同一个测试模型中,通过参数化变量控制不同测试点。例如定义一个结构体参数 testCfg,字段包括 stepAmp、stepTime、tolerance、settleTime,每个测试点对应一组取值。

testCfg.stepAmp    = 3000;   % rpm
testCfg.stepTime   = 0.5;    % s
testCfg.tolerance  = 0.02;   % ±2%
testCfg.settleTime = 0.2;    % s
testCfg.loadTorque = 5;      % N·m

建模时要把“预期输出”也参数化,而不是写死在判定模块里。例如稳态误差带用 tolerance 计算上下限,恢复时间用 settleTime 作为判定窗口。这样同一套模型可以通过换参数覆盖多个测试点,避免为每个测试点单独搭模型。

自动生成:从模型到可执行用例集

自动生成环节要做三件事:批量生成参数组合、批量生成测试模型实例、批量生成执行脚本。常见做法是用 MATLAB 脚本遍历测试点清单,为每个测试点生成一个 Simulink.SimulationInput 对象,把参数写入其中,再统一提交给 parsim 或 sim 执行。

for i = 1:numel(tpList)
    in(i) = Simulink.SimulationInput('sldemo_mdl');
    in(i) = in(i).setVariable('testCfg', tpList(i).cfg);
    in(i) = in(i).setVariable('tpId', tpList(i).id);
end
out = parsim(in, 'ShowProgress', 'on');

生成阶段还要处理“用例集的组织方式”:按测试点编号命名结果文件、按优先级分组、按依赖关系排序。如果某些测试点需要预热或需要前一个测试点的稳态作为初始条件,必须在生成阶段就记录依赖关系,否则执行调度会得到错误结果。

执行调度:让用例集稳定跑起来

执行调度解决的是“什么时候跑、跑多少、资源怎么分”。在 Simulink 测试中,常见调度策略包括:串行执行(适合有状态依赖的用例)、并行执行(适合相互独立的用例)、分批执行(适合资源受限场景)。调度时要明确每个用例的超时时间、失败重试次数、结果保存路径。

例如 20 个测试点,其中 5 个有状态依赖必须串行,其余 15 个可以并行。调度器应先把 5 个串行用例排成一条链,再把 15 个独立用例分配到并行池中。每个用例执行完成后,把 tpId、通过状态、关键指标(上升时间、超调量、稳态误差)写入结果表。

结果判定与报告闭环

结果判定不是简单看“仿真是否报错”,而是按测试点定义的判定条件逐项检查。以 TP-SPD-001 上升时间为例,判定逻辑是:在阶跃时刻之后,实际转速首次进入 90% 目标值的时间是否小于 200 ms。判定结果应输出三态:通过、失败、不确定(如仿真未收敛)。

报告闭环的关键是“失败可追溯”。每个失败用例要能反向映射到测试点编号、关联需求编号、参数配置和仿真结果文件。如果某个需求下的所有测试点都失败,说明可能是需求理解偏差或模型接口错误,而不是单个用例问题。闭环的最终产出是一张覆盖率表:需求覆盖率、测试点通过率、失败用例清单、待澄清需求清单。

整条流程中,需求分析和测试点提取决定了“测什么”,用例建模和自动生成决定了“怎么表达和批量生产”,执行调度和结果判定决定了“怎么跑和怎么判”。六个环节的输入输出必须显式定义,否则自动化只会把错误放大。

把这条流程落到工程实践时,建议先用手工方式跑通一个测试点的完整链路:从需求拆边界、提测试点、建参数化模型、生成一个用例、执行一次、判定一次、出一份报告。确认链路无误后,再把测试点清单扩展到全量,用脚本批量生成和执行。这样做的原因是,流程中任何一个环节的接口定义错误,在单测试点阶段最容易发现,一旦扩展到几十个用例,排查成本会成倍上升。