逻辑覆盖 Logic Coverage

程序中的逻辑 Logic in Program

((x > 5) && (y > 0))

判定 / 决策 Decision

整个布尔表达式是一个decision

条件 Condition

表达式中的每个基本布尔子表达式是 condition。

判定覆盖 / 决策覆盖 Decision Coverage

Decision Coverage,DC

要求判定的结果至少执行一次 true,至少执行一次 false。

对于((x > 5) && (y > 0))需要让整个decision的结果分别为truefalse

例如数据集 // {(6, 1), (1, 1)} -- DC

代码:

int foo(int x, int y) {
    int z = y;
 
    if ((x > 5) && (y > 0)) {
        z = x;
    }
 
    return x * z;
}

输入(6,1)整个decision为true

输入(1,1)整个decision为false

所以这组测试满足 判定覆盖 DC。

条件覆盖 Condition Coverage

要求每个 condition 都至少取到一次 true,至少取到一次 false。

对于:

(x > 5)
(y > 0)

需要满足:

(x > 5):true 和 false
(y > 0):true 和 false

输入(6,0)x > 5为true,y > 0为false

输入(0,1)x > 5为false,y > 0为true

因此每个条件都出现过 true 和 false,满足 条件覆盖 CC。

注意:这组测试满足 CC,但不一定满足 DC。因为两个输入下整个 decision 都是 false,没有让整个 decision 取到 true。

包含 / 蕴含 / 覆盖准则的强弱关系 Subsume

如果测试准则 C1 包含测试准则 C2,记作:C1 ≥ C2

判定覆盖 DC ≥ 语句覆盖 SC
条件覆盖 CC 不一定 ≥ 语句覆盖 SC
判定覆盖 DC 不一定 ≥ 条件覆盖 CC
条件覆盖 CC 不一定 ≥ 判定覆盖 DC

条件/判定覆盖

把 DC 和 CC 结合起来。

C/DC 同时要求满足:

  • 判定覆盖 DC
  • 条件覆盖 CC

因此

C/DC ≥ CC
C/DC ≥ DC

如果一个测试集满足 C/DC,那么它一定满足 CC,也一定满足 DC。

(6, 1)
(6, 0)
(0, 1)

多条件覆盖 Multiple Condition Coverage

检查布尔子表达式的每一种可能组合是否都出现过。MCC

多条件覆盖用于判断布尔子表达式的所有可能取值组合是否都被测试到了。

为了达到完整的多条件覆盖,需要的测试用例本质上就是该逻辑表达式的真值表。

理论上要覆盖2^n种条件组合。

对于((x > 5) && (y > 0))

真值表如下:

x > 5y > 0Decision
TTT
TFF
FTF
FFF

代码如下:

// ??
int foo(int x, int y) {
    int z = y;
 
    if ((x > 5) && (y > 0)) {
        z = x;
    }
 
    return x * z;
}

这里需要构造满足 MCC 的测试用例,也就是让两个条件的 4 种组合都出现。

测试输入x > 5y > 0Decision
(6, 1)TTT
(6, 0)TFF
(1, 1)FTF
(1, 0)FFF
  • MCC:Multiple Condition Coverage,多条件覆盖
  • C/DC:Condition/Decision Coverage,条件/判定覆盖
  • MC/DC:Modified Condition/Decision Coverage,修正条件/判定覆盖

修正条件/判定覆盖 MC/DC

MC/DC全称是:Modified Condition/Decision Coverage

它比 C/DC 更严格,核心思想是:每一个基本条件都应该被证明能够 独立影响整个判定结果。

MC/DC 要求每个条件都能分别执行到 true 和 false,并且每个条件都能够 独立影响整个判定结果。

也就是说,对于一个 decision 中的每个 condition,都要证明:

当其他条件保持不变时,只改变这个条件的取值,整个 decision 的结果也随之改变。

MC/DC ≥ C/DC MC/DC包含C/DC

也就是说,如果一个测试集满足 MC/DC,那么它一定满足 C/DC。

To test if A and (B or C)

A && (B || C)

MC/DC的目标是分别证明

  • A 能独立影响整个 decision;
  • B 能独立影响整个 decision;
  • C 能独立影响整个 decision。

不是简单让 A、B、C 都出现 true 和 false 就够了,而是要找成对的测试用例,让某一个条件变化时,其他条件保持不变,并且 decision 结果发生变化。

int func(BOOL A, BOOL B, BOOL C)
{
    if (A && (B || C))
        return 1;
    return 0;
}

对应关系

Condition A: case 1 and case 2
Condition B: case 1 and case 3
Condition C: case 3 and case 4

构造如下测试表

CaseABCB || CDecision = A && (B || C)
case 1TTFTT
case 2FTFTF
case 3TFFFF
case 4TFTTT

MC/DC:讨论

MC/DC 最初是为那些逻辑运算符 不会短路求值 的语言设计的。

C、C++ 和 Java 中的短路逻辑运算符,只会在某个条件的结果可能影响整个 decision 时才继续求值。

A && B如果A已经是false,那么整个表达式一定是false,因此B不会再被求值。

MC/DC 会受到程序中 decision 结构的影响。

同样的逻辑表达式,如果写法不同,比如写成一个复合条件,或者写成嵌套 if,实际执行路径和条件求值情况可能不同,因此 MC/DC 分析也可能不同。

写法一:复合条件

// ??
int foo(int x, int y) {
    int z = y;
 
    if ((x > 5) && (y > 0)) {
        z = x;
    }
 
    return x * z;
}

写法二:嵌套 if

// ??
int foo(int x, int y) {
    int z = y;
 
    if (x > 5) {
        if (y > 0) {
            z = x;
        }
    }
 
    return x * z;
}

两者在功能上等价,都表示if ((x > 5) && (y > 0))

但是从覆盖角度看,结构不同:

  • 第一种是一个 decision,里面有两个 condition;
  • 第二种是两个嵌套 decision,每个 decision 只有一个 condition。

因此 MC/DC 的分析对象会发生变化。

对于第一种:

只有一个decision:D = (x > 5) && (y > 0)

有两个condition:C1 = x > 5 C2 = y > 0

对于第二种:

有两个decision:D1 = x > 5 D2 = y > 0

Quiz

请构造一个 decision,使得其中某个 condition 没有独立影响结果的能力。

我们可以构造A || (A && B)

这是吸收率,A || (A && B) = A,整个decision实际上只由A决定。

ABA && BA || (A && B)
TTTT
TFFT
FTFF
FFFF

Discussion

覆盖准则大致需要的测试数量
DC2
CC2 × n
MC/DC2 × n
MCC2ⁿ

其中 n 是 decision 中 condition 的数量。

DC

只要求整个 decision 取到 true 和 false,所以通常 2 个测试用例就可能满足。

CC

要求每个 condition 都取到 true 和 false。

如果有 n 个 condition,粗略看需要覆盖每个条件的两种取值,所以写作 2 * n。

不过实际最少测试数可能小于 2 * n,因为一个测试用例可以同时让多个 condition 取到某种值。

MC/DC

要求每个 condition 都能独立影响 decision。

通常测试数量随 n 线性增长,课件写作 2 * n。

MCC

要求所有条件组合都出现。

如果有 n 个 condition,那么所有组合数量是:2^n

测试 A xor B

MCC,需要覆盖4种组合

(T, T)
(T, F)
(F, T)
(F, F)

如果按照 MC/DC,也可以用这 4 个组合说明 A 和 B 都能独立影响结果:

A 的独立影响

ABA xor B
TTF
FTT

B 的独立影响

ABA xor B
TTF
TFT