第3章:单元测试方法论:白盒测试基础、语句覆盖、分支覆盖、MC/DC覆盖、条件覆盖
好,咱们今天聊聊单元测试里最核心的东西——白盒测试。说实话,很多刚入行的工程师觉得白盒测试就是“看代码写测试”,这话对了一半。白盒测试的精髓,是你要知道代码怎么跑,然后设计用例去验证它。
我个人习惯把白盒测试比作“拆解一台发动机”。你不仅要知道它能不能转,还得知道每个活塞、每个气门是怎么配合的。嗯,咱们今天就把这些“零件”拆开看看。
白盒测试基础:你凭什么相信代码?
白盒测试,说白了就是基于代码内部结构的测试。你看到了if、else、for、while,你就得想:这些分支我测全了吗?
我记得有一次做安全气囊控制器的测试,一个简单的条件判断没覆盖到,结果在某个极端情况下气囊该爆没爆。那次之后,我对白盒测试的敬畏心就上来了。
白盒测试的核心指标就是覆盖率。常见的覆盖率有几种:
- 语句覆盖:每行代码都执行过
- 分支覆盖:每个判断的真假分支都走过
- 条件覆盖:每个原子条件的真假都测过
- MC/DC覆盖:每个条件能独立影响结果
你想想看,如果连语句都没跑全,你怎么敢说代码没问题?
语句覆盖:最基础,但别小看它
语句覆盖的要求很简单:让每一条可执行语句至少执行一次。听起来容易,对吧?
看个例子:
int calculate(int a, int b) {
int result = 0;
if (a > 0) {
result = a + b;
}
return result;
}
如果你只传 calculate(1, 2),那 result = a + b 这行执行了,没问题。但如果你传 calculate(-1, 2),那这行就没执行。所以语句覆盖要求你至少有一个用例让 a > 0 成立。
分支覆盖:让每个岔路口都走一遍
分支覆盖比语句覆盖更进一步。它要求每个判断的真假分支都至少执行一次。
还是刚才的例子:
if (a > 0) {
result = a + b;
} else {
result = a - b;
}
分支覆盖要求你至少有两个用例:一个让 a > 0 为真,一个让 a > 0 为假。这样两个分支都走到了。
我曾经在一个项目中遇到过这样的坑:一个if-else结构,测试只测了真分支,假分支的逻辑里有个指针没判空,结果上线就崩了。嗯,从那以后我特别强调分支覆盖。
条件覆盖:拆到最小单元
条件覆盖要求每个原子条件(比如 a > 0、b == 1)都取过真和假。
看个复杂点的:
if (a > 0 && b == 1) {
// do something
}
这里有两个原子条件:a > 0 和 b == 1。条件覆盖要求:
a > 0为真和假都测过b == 1为真和假都测过
但注意,条件覆盖不要求这两个条件组合起来覆盖所有分支。比如你测了 (a>0为真, b==1为假) 和 (a>0为假, b==1为真),两个条件都取过真假了,但整个if语句可能一次都没进去过(因为 && 要求两个都为真)。
这就是条件覆盖的局限性。所以有了MC/DC。
MC/DC覆盖:汽车行业的硬门槛
MC/DC,全称是 Modified Condition/Decision Coverage。翻译过来就是“修正条件/判定覆盖”。名字挺绕,但核心思想很简单:每个条件都能独立地影响整个判断的结果。
为什么汽车行业这么看重MC/DC?因为安全。ISO 26262里明确要求,ASIL C和D等级的功能安全软件,MC/DC覆盖率必须达到100%。
MC/DC的要求有几点:
- 每个原子条件都取过真和假
- 每个判断都取过真和假
- 每个条件都能独立影响判断结果——也就是说,改变这个条件,其他条件不变,结果会变
看个例子:
if (A && B) {
// do something
}
要满足MC/DC,你需要两个用例:
| 用例 | A | B | 结果 | 说明 |
|---|---|---|---|---|
| 1 | 真 | 真 | 真 | 进入if |
| 2 | 真 | 假 | 假 | B独立影响结果 |
| 3 | 假 | 真 | 假 | A独立影响结果 |
你看,用例2和3分别证明了B和A能独立影响结果。这就是MC/DC的精髓。
实际项目中的选择
在ASPICE流程中,不同安全等级对覆盖率的要求不同:
| 安全等级 | 推荐覆盖率 | 说明 |
|---|---|---|
| QM | 语句覆盖 100% | 基本要求 |
| ASIL A | 分支覆盖 100% | 需要验证所有分支 |
| ASIL B | 条件覆盖 100% | 需要验证每个条件 |
| ASIL C/D | MC/DC 100% | 最高要求,安全关键 |
我个人建议,即使项目要求不高,也尽量往MC/DC靠。为什么?因为MC/DC能发现那些“隐藏很深”的逻辑错误。我曾经在一个制动控制模块中,用MC/DC发现了一个条件短路导致的逻辑漏洞——那个bug在语句覆盖和分支覆盖下都测不出来。
好了,这一章的内容就到这儿。白盒测试的几种覆盖方法,从语句覆盖到MC/DC,一层比一层严格。你想想看,你的项目现在做到哪一层了?下一章咱们聊聊集成测试,看看怎么把这些单元拼起来验证。