3. HIL测试基础

各位做HIL测试的同行,咱们今天聊点实在的。覆盖度度量,说白了就是回答一个问题:你的测试到底测透了没有?

我见过不少项目,测试报告写得漂漂亮亮,用例数量上千条,结果一上实车就出问题。为什么?因为覆盖度指标没选对,或者选了但没理解透。

这一章,咱们就把四个最核心的覆盖度指标掰开揉碎。不光是数学定义,更重要的是——在HIL台架上,这些指标到底能帮你发现什么问题?

3.1 语句覆盖(Statement Coverage)

数学定义:

语句覆盖 = (被执行的语句数 / 总可执行语句数) × 100%

举个例子:

if (speed > 30 && brake_pressed) {
    // 语句A: 激活紧急制动辅助
    activateEBA();
}
// 语句B: 更新制动状态
updateBrakeStatus();

如果测试用例只让 speed > 30 && brake_pressed 为真,那么语句A和B都执行了,语句覆盖100%。

工程意义:

  • 最低门槛:我个人习惯把语句覆盖当作“及格线”。达不到100%,说明有代码根本没跑过。
  • 局限性明显:语句覆盖100%,不代表分支逻辑都测了。上面的例子,如果条件为假,语句A没执行,但语句B执行了——语句覆盖还是100%。
避坑指南:我曾经在一个项目中,语句覆盖做到了98%,结果漏掉了一个else分支。那个分支处理的是传感器故障模式,实车测试时直接触发了安全停机。从那以后,我要求团队必须把语句覆盖和分支覆盖一起看。

3.2 分支覆盖(Branch Coverage)

数学定义:

分支覆盖 = (被执行的分支数 / 总分支数) × 100%

每个判断点(if/else、switch/case、while等)都有两个或以上的出口。分支覆盖要求每个出口至少被执行一次。

还是刚才的例子:

if (speed > 30 && brake_pressed) {
    // 分支真: 激活紧急制动辅助
    activateEBA();
} else {
    // 分支假: 记录日志
    logCondition();
}

要达到100%分支覆盖,需要两个用例:

  • 用例1: speed > 30 且 brake_pressed 为真 → 走真分支
  • 用例2: speed ≤ 30 或 brake_pressed 为假 → 走假分支

工程意义:

  • 比语句覆盖更严格:分支覆盖能发现“漏掉的else”。
  • HIL测试中的价值:很多控制器的故障处理逻辑都在else分支里。你想想看,如果只测正常工况,故障分支永远跑不到,那HIL测试的意义就大打折扣了。
我的经验:在HIL测试中,分支覆盖建议做到100%。特别是安全相关的功能(如ASIL C/D等级),这是硬性要求。我一般会在测试规范里明确:每个if-else、每个switch-case都必须有对应的测试用例。

3.3 条件覆盖(Condition Coverage)

数学定义:

条件覆盖 = (被取值为真和假的原子条件数 / (原子条件总数 × 2)) × 100%

注意,条件覆盖关注的是原子条件,而不是整个复合条件。

看这个表达式:speed > 30 && brake_pressed

它有两个原子条件:

  • C1: speed > 30
  • C2: brake_pressed

要达到100%条件覆盖,需要让C1取真和假各一次,C2取真和假各一次。最少两个用例就能做到:

  • 用例1: speed=50, brake_pressed=true → C1真, C2真
  • 用例2: speed=20, brake_pressed=false → C1假, C2假

工程意义:

  • 更细粒度:条件覆盖能发现“某个传感器信号是否被正确激励”。
  • HIL场景:在HIL测试中,每个原子条件往往对应一个物理信号(车速、制动开关、温度等)。条件覆盖帮你确认:每个信号的高/低电平状态都被测到了。
重要提醒:条件覆盖100% ≠ 分支覆盖100%。上面的两个用例虽然条件覆盖100%,但分支覆盖只有50%(只走了真分支,没走假分支)。所以,条件覆盖和分支覆盖要配合使用。

3.4 MC/DC覆盖(Modified Condition/Decision Coverage)

数学定义:

MC/DC覆盖要求:

  1. 每个入口点和出口点至少被执行一次。
  2. 每个判断(Decision)的所有可能结果至少取一次。
  3. 每个条件(Condition)的所有可能结果至少取一次。
  4. 每个条件能独立地影响判断的结果——即改变该条件而不改变其他条件时,判断结果随之改变。

这是最严格的覆盖标准。咱们用真值表来理解:

用例 C1: speed > 30 C2: brake_pressed 判断结果 (C1 && C2) C1独立影响? C2独立影响?
1 真 真 真 — —
2 假 真 假 ✓ (对比用例1) —
3 真 假 假 — ✓ (对比用例1)

三个用例就能达到MC/DC 100%。每个条件都独立地翻转了一次判断结果。

工程意义:

  • 航空/汽车安全标准强制要求:DO-178C(航空)和ISO 26262(汽车)中,ASIL C/D等级的功能通常要求MC/DC覆盖。
  • 发现“隐藏的依赖”:有些条件看似独立,实际在代码中有耦合。MC/DC能帮你揪出来。
  • 测试用例数量可控:对于n个原子条件,MC/DC最少只需要n+1个用例(如上例,2个条件需要3个用例)。
我曾经踩过的坑:在一个ESP控制器的项目中,我们按MC/DC标准设计了测试用例。结果发现,某个条件(车速 > 5 km/h)在代码中被重复判断了三次,每次的阈值还不同。MC/DC覆盖虽然100%,但实际逻辑有歧义。所以,MC/DC是手段,不是目的——它帮你发现测试盲区,但不能替代代码审查。

3.5 四种覆盖度的对比与选择

咱们用一张表来总结:

覆盖度指标 关注点 严格程度 HIL测试推荐场景 最少用例数(n个原子条件)
语句覆盖 每行代码是否执行 低 冒烟测试、快速验证 1
分支覆盖 每个判断出口是否走到 中 功能测试、回归测试 2
条件覆盖 每个原子条件是否取真/假 中高 信号级测试、传感器故障注入 2
MC/DC覆盖 每个条件能否独立影响结果 高 安全关键功能(ASIL C/D) n+1

我的建议:

  • 项目初期:先跑语句覆盖,确保基本功能通顺。
  • 功能测试阶段:用分支覆盖 + 条件覆盖,覆盖正常和异常路径。
  • 安全关键功能:必须上MC/DC。别嫌麻烦,这是保命的。

3.6 知识体系总览

下面这张图,帮你把四个指标的关系理清楚:

覆盖度度量指标知识体系 覆盖度度量指标知识体系 覆盖度度量指标 语句覆盖 分支覆盖 条件覆盖 MC/DC覆盖 严格程度:语句覆盖 < 分支覆盖 ≈ 条件覆盖 < MC/DC覆盖 工程应用:冒烟测试 → 功能测试 → 信号级测试 → 安全关键功能测试 选择策略 • 项目初期:语句覆盖(快速验证) • 功能测试:分支覆盖 + 条件覆盖(覆盖正常/异常路径) • 安全关键:MC/DC覆盖(ASIL C/D强制要求)

3.7 工程实践中的常见误区

最后,分享几个我见过的典型问题:

  • 误区一:追求100%覆盖度就万事大吉
    覆盖度是必要条件,不是充分条件。逻辑正确性、时序问题、边界值,覆盖度指标管不了。
  • 误区二:MC/DC覆盖只适用于软件单元测试
    不对。在HIL测试中,MC/DC同样适用——只要你能精确控制每个输入信号。我就在一个BMS项目中用HIL实现了MC/DC覆盖,发现了电池过温保护逻辑的漏洞。
  • 误区三:所有功能都要MC/DC
    没必要。MC/DC的测试用例设计成本高,只建议用于安全关键功能。普通功能用分支覆盖+条件覆盖就够了。
我的习惯:在制定HIL测试计划时,我会先明确每个功能的安全等级。ASIL A/B用分支覆盖,ASIL C/D上MC/DC。然后根据覆盖度目标反推测试用例数量,再评估台架资源是否够用。这样,测试计划才有可执行性。