白盒测试——动态测试——逻辑覆盖法
目录
1. 语句覆盖 (Statement Coverage)
2. 判定覆盖 (Decision Coverage) / 分支覆盖
3. 条件覆盖 (Condition Coverage)
4. 判定/条件覆盖 (Decision/Condition Coverage)
5. 条件组合覆盖 (Multiple Condition Coverage)
6. 路径覆盖 (Path Coverage)
总结与对比
白盒测试的逻辑覆盖法听起来名词很多,容易混淆,但其实它们就像是“给代码做体检的不同颗粒度”——从粗到细,看看你的测试用例到底跑过了代码的哪些角落。
为了让你更好地理解,我们先设定一段经典的、带有两个判断逻辑的示例代码作为我们的“靶子”。
void testLogic(int a, int b, int x) { // 判定 1 (包含条件 C1 和 C2) if (a > 1 && b == 0) { x = x / a; // 语句 1 } // 判定 2 (包含条件 C3 和 C4) if (a == 2 || x > 1) { x = x + 1; // 语句 2 } }在这段代码中:
判定 (Decision/Branch):指的是整个
if后面的大括号里的判断结果(真或假)。这里有两个判定:判定1和判定2。条件 (Condition):指的是构成判定的最小逻辑单元。这里有四个条件:
C1: a>1、C2: b==0、C3: a==2、C4: x>1。
接下来,我们用这段代码把这 6 种覆盖法逐一击破。
1. 语句覆盖 (Statement Coverage)
目标:让程序里每一行可执行的代码,至少都执行一次。
通俗理解:走马观花,每一行代码我都要走到。这是最基础的覆盖,强度最弱。
用例设计:我们只需要让
判定1和判定2都为真,就能走进大括号里执行语句1和语句2。用例:
a = 2, b = 0, x = 4执行过程:*
判定1: 2 > 1 且 0 == 0(真),执行x = 4 / 2 = 2。(覆盖了语句1)判定2: 2 == 2 或 2 > 1(真),执行x = 2 + 1 = 3。(覆盖了语句2)
缺点:无法发现隐藏在分支(比如条件为假时)里的逻辑错误。
2. 判定覆盖 (Decision Coverage) / 分支覆盖
目标:让程序里每一个判定(if)的“真 (True)”和“假 (False)”分支都至少走一次。
通俗理解:每一个路口的左拐和右拐,我都要走一遍。
用例设计:需要
判定1 (T, F)和判定2 (T, F)。用例1:
a = 2, b = 0, x = 4-> 判定1(真),判定2(真)。用例2:
a = 1, b = 1, x = 1-> 判定1(假),判定2(假)。
缺点:判定覆盖只看最终结果,不看判定内部的条件。比如
判定1是假,可能是因为a<=1,也可能是因为b!=0,它没有对内部细节进行充分测试。
3. 条件覆盖 (Condition Coverage)
目标:让判定里面的每一个独立条件,都至少取一次“真”和“假”。
通俗理解:我不管你整个判定是真是假,我只要求组成判定的各个小零件(C1, C2, C3, C4)都分别变过一次红绿灯。
用例设计:
C1:
a>1(T/F)C2:
b==0(T/F)C3:
a==2(T/F)C4:
x>1(T/F) (注意这里的 x 是经过第一步运算后的 x)用例1:
a = 2, b = 0, x = 4-> C1(T), C2(T), 此时 x=2 -> C3(T), C4(T)。用例2:
a = 1, b = 1, x = 1-> C1(F), C2(F), 此时 x=1 -> C3(F), C4(F)。
盲区提醒:满足了条件覆盖,不一定满足判定覆盖!如果有个判定是
if (A || B),你设计的用例是(A=T, B=F)和(A=F, B=T),满足了条件覆盖,但整个判定结果都是 True,缺少了 False 分支的覆盖。
4. 判定/条件覆盖 (Decision/Condition Coverage)
目标:小孩子才做选择,大人全都要。既要满足“判定覆盖”,又要满足“条件覆盖”。
通俗理解:每个路口的左右拐要走一遍,且决定拐向的每一个小零件的正反面也要翻一遍。
用例设计:刚好上面【3. 条件覆盖】中设计的两个用例
a=2, b=0, x=4和a=1, b=1, x=1,不仅让所有小条件都取了真假,同时也让判定1和判定2的整体结果都取了真假。所以这组用例也满足判定/条件覆盖。
5. 条件组合覆盖 (Multiple Condition Coverage)
目标:针对每一个判定内部,所有可能的“条件真假组合”都要执行一次。
通俗理解:穷举法。判定1有两个条件,就有 2x2=4 种组合;判定2有两个条件,也有 4 种组合。
用例设计:* 对于
判定1 (a>1, b==0),需要覆盖:(T,T), (T,F), (F,T), (F,F)。对于
判定2 (a==2, x>1),需要覆盖:(T,T), (T,F), (F,T), (F,F)。你需要设计多个用例,确保这 8 种局部组合都被触发过。这是非常强的一种覆盖方式,但用例数量会显著增加。
6. 路径覆盖 (Path Coverage)
目标:覆盖程序中所有可能的完整执行路径。
通俗理解:从起点到终点,不管中间怎么绕,所有能走通的完整路线都要走一遍。
用例设计:我们的代码有两个连续的判断,相当于两条连续的分岔路,因此有 $2 \times 2 = 4$ 条路径:
判定1(真) -> 判定2(真)
判定1(真) -> 判定2(假)
判定1(假) -> 判定2(真)
判定1(假) -> 判定2(假)
特点:路径覆盖能发现最深层次的组合错误,但如果代码里有循环(比如
while循环跑 100 次),路径数量会爆炸(无穷大),所以实际中通常会对循环次数进行限制。
总结与对比
| 覆盖方法 | 关注焦点 | 覆盖强度 | 缺陷 |
| 语句覆盖 | 每一行代码 | 弱 | 最基础,漏测率极高 |
| 判定(分支)覆盖 | T/F 结果 | 较弱 | 忽略了内部复杂的条件组合 |
| 条件覆盖 | 每个内部条件 | 较弱 | 可能会漏掉判定的某个分支 |
| 判定/条件覆盖 | T/F 结果 + 内部条件 | 中等 | 依然没有覆盖条件的所有组合 |
| 条件组合覆盖 | 内部条件的各种组合 | 强 | 用例数量多,设计成本高 |
| 路径覆盖 | 完整的执行流 | 最强 (理论上) | 存在循环时,用例数呈指数级爆炸 |
