2. 测试体系架构:HIL/SIL/MIL测试概念、测试金字塔模型、测试策略制定

好,咱们进入第二章。这一章我打算聊聊测试体系的架构。说白了,就是回答一个问题:这么多测试,到底怎么组织才不乱?

我刚入行那会儿,测试就是“焊块板子,上电,看波形”。后来项目越来越复杂,光靠手工点灯根本扛不住。你想想看,一个仪表盘项目,几十个信号、上百条CAN报文、还有各种故障注入……没有体系,那就是一团乱麻。

所以,这一章的核心就是三个东西:MIL/SIL/HIL 的概念、测试金字塔模型、以及怎么制定测试策略。嗯,咱们一个一个来。

2.1 MIL / SIL / HIL:三个层次的测试概念

这三个缩写,你肯定见过。但很多人只是背概念,不知道它们到底怎么用。我用自己的理解给你拆开讲。

2.1.1 MIL(Model-in-the-Loop)—— 模型在环

MIL 是最早的环节。这时候连代码都没有,只有模型。比如你用 Simulink 搭了一个车速计算逻辑,那就在这个模型环境里跑测试。

为什么要做 MIL?

  • 验证算法逻辑对不对。比如:车速超过120km/h,报警灯亮不亮?
  • 发现设计缺陷。我在项目中遇到过,模型里有个除法,分母可能为零,MIL 阶段就抓出来了。
  • 成本最低。改模型比改代码快多了。
我的习惯:MIL 阶段不要只跑“正常用例”。多跑边界值,比如车速刚好等于阈值、信号刚好丢失。这时候发现问题,后面能省很多事。

2.1.2 SIL(Software-in-the-Loop)—— 软件在环

代码写好了,但还没烧到硬件上。这时候把代码编译成 PC 上能跑的动态库,然后用测试脚本去调用它。

SIL 的价值在哪?

  • 验证代码实现和模型是否一致。说白了,就是“代码有没有把模型的意思搞歪”。
  • 可以跑大量自动化测试。我做过一个项目,SIL 阶段跑了 2000 多个用例,一晚上跑完。要是等硬件出来再测,得测一个月。
  • 调试方便。代码崩了,直接看堆栈,不用接示波器。
注意:SIL 环境毕竟不是真实硬件。有些时序问题、中断问题,SIL 是测不出来的。别指望 SIL 能替代 HIL。

2.1.3 HIL(Hardware-in-the-Loop)—— 硬件在环

这是最接近实车的测试。把真实的 ECU(仪表盘硬件)接上,用一台实时仿真机模拟车辆的其他信号(比如发动机转速、车速、灯光状态)。

HIL 测什么?

  • 硬件接口:IO 口电平对不对?PWM 波形准不准?
  • 通信协议:CAN/LIN 报文收发是否正常?
  • 故障注入:比如某个传感器短路了,仪表盘会不会正确报警?
  • 时序和实时性:从收到 CAN 报文到屏幕刷新,延迟多少毫秒?

我记得有一次做 HIL 测试,发现仪表盘在收到连续高速 CAN 报文时,偶尔会丢一帧。这个问题在 SIL 阶段完全没暴露,因为 PC 的处理速度比真实 MCU 快太多了。这就是 HIL 不可替代的原因。

测试层级 测试对象 主要目的 执行速度 成本
MIL 模型 验证算法逻辑 最快 最低
SIL 代码(PC环境) 验证代码实现
HIL 真实硬件 验证硬件接口和实时性 慢(实时运行)

2.2 测试金字塔模型

这个概念最早来自软件测试领域,但用在仪表盘测试上,同样适用。说白了就是:底层测试要多,上层测试要少。

为什么?因为底层测试(比如单元测试、MIL/SIL)跑得快、成本低、发现问题早。上层测试(比如 HIL、系统测试)跑得慢、成本高、发现问题晚。

一个典型的仪表盘测试金字塔,从下往上大概是这样的:

  1. 底层:单元测试 / MIL 测试 —— 数量最多,覆盖每个函数、每个模型模块。
  2. 中间层:集成测试 / SIL 测试 —— 验证模块之间的交互,比如 CAN 接收模块和显示模块的配合。
  3. 顶层:系统测试 / HIL 测试 —— 验证整个仪表盘作为一个系统,是否满足需求。

我的经验:很多团队把 HIL 测试当成“主力”,花大价钱买设备,结果底层测试几乎不做。这是典型的倒金字塔。我曾经接手过一个项目,HIL 测试天天报 bug,一查,全是底层逻辑问题。如果 MIL 阶段多花两天,HIL 阶段能省两周。

所以,我建议的比例是:MIL/SIL 测试占 60%,集成测试占 25%,HIL 测试占 15%。当然,这个比例不是死的,但方向一定要对。

2.3 测试策略制定

有了概念,有了模型,接下来就是怎么落地。测试策略,说白了就是回答三个问题:

  • 测什么? —— 测试范围
  • 怎么测? —— 测试方法
  • 测多少? —— 测试深度

2.3.1 基于风险的测试策略

我个人最推崇的方法。不是所有功能都值得花同样的精力去测。比如:

  • 高优先级:安全相关功能(车速显示、报警灯、故障提示)。这些必须全覆盖,包括正常、边界、异常。
  • 中优先级:常用功能(里程显示、油耗计算)。覆盖主要场景,边界值可以抽测。
  • 低优先级:辅助功能(主题切换、语言设置)。覆盖正常场景即可。

我曾经犯过一个错误:在一个项目里,把所有功能都按“高优先级”去测,结果测试周期拖了两个月,最后客户催得紧,反而漏掉了几个关键的安全用例。嗯,从那以后,我学会了“抓大放小”。

2.3.2 测试用例设计原则

不管哪个层级,测试用例的设计都有一些通用原则:

  • 等价类划分:把输入分成几个“等价”的区间,每个区间选一个代表。比如车速 0-60 是一个区间,60-120 是另一个区间。
  • 边界值分析:bug 往往出现在边界上。比如报警阈值是 120km/h,那 119、120、121 这三个值必须测。
  • 错误推测:凭经验猜哪里容易出问题。比如 CAN 总线突然断开、电源电压波动。
一个小技巧:写测试用例时,不要只写“输入 A,期望输出 B”。还要写清楚“为什么这么测”。这样别人 review 的时候能理解你的意图,也方便后续维护。

2.3.3 自动化测试策略

仪表盘测试,不做自动化,迟早累死。但自动化也不是万能的。我的建议是:

  • MIL/SIL:必须全自动化。脚本跑一遍,结果自动比对。我习惯用 Python + Simulink Test 或者 VectorCAST。
  • HIL:尽量自动化。但有些场景(比如视觉检查屏幕显示内容)还是需要人工介入。不过现在也有图像识别工具可以辅助。
  • 回归测试:每次代码变更后,自动跑一遍核心用例。这个投入产出比最高。

我记得有一次,一个项目改了 CAN 报文解析的底层代码,结果把仪表盘的里程显示搞坏了。幸好我们有自动化回归测试,第二天一早就发现了。要是靠人工测,可能到交付前才发现,那就惨了。

2.4 小结

这一章的内容,说白了就是三句话:

  1. MIL/SIL/HIL 是三个不同层次的测试,各有各的用处,谁也替代不了谁。
  2. 测试金字塔告诉我们,底层测试要多做,上层测试要精做。
  3. 测试策略的核心是“基于风险”,把有限的资源花在刀刃上。

下一章,我会讲具体的测试环境搭建。到时候咱们聊聊怎么搭一个靠谱的 HIL 台架,以及那些年我踩过的坑。嗯,先到这儿吧。