路径覆盖可以使程序中的路径都被测试到,但是,要对程序中的路径做到完全覆盖经常是无法实现的。为了解决这一难题,我们需要在保证测试质量的前提下把测试的路径数量压缩到一定的范围内
基本路径覆盖法是在程序控制流图的基础上,通过分析控制结构的圈复杂度,导出基本可执行的路径集合设计测试用例,运行被测程序,使程序的基本路径都得到覆盖。基本路径覆盖法把测试时需要覆盖的路径压缩到一定的范围内,使程序中的每一个可执行语句都至少执行一次,程序中的循环体最多只执行一次。本实验主要通过实例介绍使用基本路径覆盖法设计白盒测试用例。
栗子
public static int test(int a, int b, int c) {
int result = 0;
if (a == 0 || b > 2) {
result = b - a;
}
if (a > 0 && c > 0) {
result = c * a;
}
return result;
}
第 1 步:分析待测试代码,画出程序的流程图。
在控制流图中如果含有复合条件,需要改为单条件嵌套的形式”,为了后续的讲述更加清晰,这里先把上述流程图中的复合条件按控制流图的要求进行拆分,具体如下:
第 2 步:根据流程图画出控制流图。
在控制流图中,圆形符号○称为“节点”,表示一个基本的代码块;包含条件的节点称为“判断节点”;箭头称为“边”,表示控制流路径,反向边则表示可能存在循环。按照控制流图的规则,上述流程图可以画成下面的控制流图:
第 3 步:计算圈复杂度。
圈复杂度 V(G) 有三种计算方法读者可以任选其中一种方法来进行计算。下面简单介绍一下用这三种方法计算本例的圈复杂度:
方法一:V(G) = A + 1,
其中 A 代表控制流图中的封闭区域数量。从下图可以看出,程序的控制流图中共有 4 个封闭区域,所以,圈复杂度 V(G) = 4 + 1 = 5 。
方法二:V(G) = P + 1
其中 P 代表控制流图中的判定节点数。从下图可以看出,程序的控制流图中共有 4 个判定节点,所以,圈复杂度 V(G) = 4 + 1 = 5 。
方法三:V(G) = e - n + 2
其中 e 代表控制流图中的边的数量,即控制流中的箭头数量;n 代表控制流图的节点数量,即控制流图中的圆圈数量。从下图中可以看出,程序的控制流图中有 11 条边(11个箭头),8个节点(8个圆圈),所以,圈复杂度 V(G) = 11 - 8 + 2 = 5 。
第 4 步:确定基本路径的集合。
基本路径又称为独立路径,是指至少包含一条其他独立路径中未包含的路径。例如,在上图中,路径 1 - 2 - 3 - 5 - 8 是一条基本路径,1 - 2 - 4 - 3 - 5 - 8 则可以看成了另外一条基本路径,因为这条路径中经过 4 节点的路径在第一条基本路径中未包含。
圈复杂度是指程序中的独立路径数量,是确保程序中每个可执行语句至少执行一次需要的测试用例数量的最小值。根据第 3 步的计算结果,本例中我们需要确定 5 条基本路径,具体如下:
路径 1 :1 - 2 - 3 - 5 - 8
路径 2 :1 - 2 - 4 - 3 - 5 - 8
路径 3 :1 - 2 - 4 - 5 - 8
路径 4 :1 - 2 - 4 - 5 - 6 - 8
路径 5 :1 - 2 - 4 - 5 - 6 - 7 - 8
第 5 步:根据基本路径编写测试用例。
根据基本路径覆盖法的定义,我们需要设计测试用例分别覆盖第 4 步中的 5 条基本路径,即设计合理的输入数据使程序运行时经过指定的路径。因此,我们可以设计如下表中的 5 个测试用例来覆盖这 5 条基本路径。
测试用例编号 | 输入数据 | 预期结果 | 路径基本覆盖情况 |
---|---|---|---|
testcase_01 | a = 0 , b = 1 , c = 9 | result = 1 | 路径 1 |
testcase_02 | a = 0 , b = 3 , c = 5 | result = 3 | 路径 2 |
testcase_03 | a = -2 , b = 1 , c = 3 | result = 0 | 路径 3 |
testcase_04 | a = 1 , b = 0 , c = -1 | result = 0 | 路径 4 |
testcase_05 | a = 5 , b = -3 , c = 2 | result = 10 | 路径 5 |
使用基本路径覆盖法设计用例进行测试时,可以使程序中的每条独立路径都至少执行一次。如果程序中的基本路径达到了 100% 覆盖,则分支(判定)覆盖、条件覆盖也能达到 100% 覆盖。如果使用基本路径覆盖法后程序中仍有未覆盖到的路径,可使用逻辑覆盖法补充测试用例保证覆盖全面。