4. 测试用例设计进阶:基于需求的测试、基于模型的测试、随机测试与形式化方法
各位工程师,欢迎来到第四章。前几章我们聊了MIL测试的基础和工具链搭建,算是把“战场”给铺好了。这一章,咱们要聊点更核心的东西——测试用例到底该怎么设计。
说实话,我见过不少团队,模型搭得漂漂亮亮,一跑测试就露馅。为什么?因为测试用例设计得不够“狠”。你想想看,如果测试用例连需求里的边界都没覆盖到,那测试还有什么意义?
这一章,我会把四种主流的测试用例设计方法掰开揉碎了讲:基于需求的测试、基于模型的测试、随机测试,还有听起来很高大上的形式化方法。每种方法我都会结合实战经验,告诉你什么时候用、怎么用、有什么坑。
核心观点:没有一种测试方法是万能的。高手不是只会一种方法,而是知道在什么场景下组合使用它们。
4.1 基于需求的测试(RBT)—— 最基础,也最容易被忽视
基于需求的测试,说白了就是“需求说啥,我就测啥”。听起来很简单对吧?但我在项目中遇到过太多次,需求文档写得模棱两可,测试人员只能靠猜。
核心思路:把每一条功能需求和非功能需求,都转化成可执行的测试用例。
4.1.1 需求的可测试性检查
在写测试用例之前,先问自己一个问题:这条需求我能测吗?
举个例子,需求写“系统响应时间要快”。什么叫快?1秒算快还是100毫秒算快?这种需求就没法直接测。必须量化成“在XX条件下,响应时间不超过XX毫秒”。
| 需求描述 | 可测试性 | 改进建议 |
|---|---|---|
| 系统应能快速处理故障 | ❌ 不可测 | 系统应在故障发生后500ms内进入安全状态 |
| 车速超过120km/h时报警 | ✅ 可测 | — |
| 系统应具有良好的稳定性 | ❌ 不可测 | 系统在连续运行72小时内不应出现非预期复位 |
4.1.2 需求覆盖矩阵
我个人的习惯是,每个项目都建一个需求-测试用例追溯矩阵。用Excel或者专门的工具都行,关键是能一眼看出来:哪条需求还没被覆盖到。
小技巧:在Simulink Test里,可以直接给测试用例打上需求标签。这样跑完测试,自动生成覆盖率报告,哪条需求没测到一目了然。
4.2 基于模型的测试(MBT)—— 让模型自己“生”出测试用例
基于模型的测试,是我个人非常喜欢的一种方法。它的核心思想是:用模型来描述系统的行为,然后自动生成测试用例。
你想想看,我们做MIL测试,模型本身就是对系统的抽象。那为什么不让这个模型帮我们生成测试用例呢?
4.2.1 MBT的工作流程
- 建立行为模型:用状态机、活动图等方式描述系统行为
- 定义覆盖准则:比如状态覆盖、转移覆盖、MC/DC覆盖
- 自动生成测试用例:工具根据模型和覆盖准则,自动生成测试序列
- 执行与评估:把生成的用例跑在Simulink模型上,看结果
注意:MBT生成的测试用例数量可能非常庞大。我曾经在一个项目中,用MBT生成了上万条测试用例,跑一次要三天。后来我加了约束条件,才把用例数降到了可接受的范围。
4.2.2 什么时候用MBT?
- 系统状态空间复杂,手动写用例不现实
- 需求变更频繁,需要快速回归测试
- 对测试覆盖率有严格要求(比如ISO 26262要求)
4.3 随机测试 —— 用“蛮力”找边界
随机测试,听起来有点“不靠谱”,但其实非常有用。它的原理很简单:在输入空间内随机生成测试数据,看看系统会不会出问题。
我记得有一次,一个同事手动写了200条测试用例,覆盖率都达标了。结果我用随机测试跑了10分钟,就发现了一个隐藏很深的bug——某个输入组合会导致除零错误。
4.3.1 随机测试的两种模式
| 模式 | 说明 | 适用场景 |
|---|---|---|
| 纯随机 | 完全随机生成输入,不依赖任何先验知识 | 早期探索,找明显bug |
| 带约束的随机 | 在合法输入范围内随机生成,排除无效组合 | 回归测试,边界测试 |
4.3.2 在Simulink中做随机测试
Simulink Test支持用MATLAB脚本生成随机输入。我一般会写一个简单的脚本,循环生成随机信号,然后自动跑仿真。
% 随机测试示例脚本
for i = 1:100
% 生成随机输入
input_signal = rand(1, 1000) * 100; % 0-100之间的随机值
% 设置测试输入
set_param('myModel/In1', 'Value', mat2str(input_signal));
% 运行仿真
sim('myModel');
% 检查结果
if any(output > 200)
disp(['发现异常:第' num2str(i) '次仿真']);
end
end
经验之谈:随机测试不能替代结构化测试,但它是发现“意外bug”的利器。我建议每个项目都跑一轮随机测试,哪怕只跑几百次,也经常能有意外收获。
4.4 形式化方法 —— 用数学证明你的系统是对的
形式化方法,听起来很学术,其实核心思想很简单:用数学语言来描述系统行为,然后证明系统满足某些性质。
我曾经在一个安全关键项目(ISO 26262 ASIL D)中用过形式化方法。说实话,学习曲线确实陡,但效果也是真明显——我们找到了几个用传统测试方法根本发现不了的逻辑错误。
4.4.1 形式化方法的三种常见形式
- 模型检验(Model Checking):穷举所有状态,验证是否满足性质
- 定理证明(Theorem Proving):用逻辑推理证明系统正确
- 抽象解释(Abstract Interpretation):对程序行为做抽象分析
4.4.2 什么时候需要形式化方法?
不是所有项目都需要形式化方法。我个人觉得,以下场景值得考虑:
- 安全完整性等级高(ASIL C/D)
- 系统逻辑复杂,传统测试无法穷举
- 需要严格的正确性证明(比如自动驾驶决策模块)
避坑指南:我曾经在一个项目里,试图用形式化方法验证整个系统。结果模型太大,工具跑了两天都没出结果。后来我把系统拆成几个小模块,分别验证,才搞定。记住:形式化方法适合小范围、高精度的验证,不适合大而全。
4.5 四种方法的对比与选择
说了这么多,到底该用哪种?我画了一张图,帮你理清思路。
4.6 实战组合策略
最后,分享一个我常用的组合策略:
- 先用基于需求的测试,确保所有功能点都被覆盖到
- 再用基于模型的测试,自动生成补充用例,提高覆盖率
- 跑一轮随机测试,看看有没有“漏网之鱼”
- 对安全关键模块,用形式化方法做精确验证
记住:测试不是为了证明系统没有bug,而是为了尽可能多地发现bug。方法只是工具,你的判断力和经验才是关键。