白盒测试用例
2.1 白盒测试用例设计
对白盒测试,目前有两种测试用例设计方法,即逻辑覆盖和基本路径测试。所谓逻辑覆盖是以程序内部的逻辑结构为基础的测试用例设计方法。根据覆盖测试的目标不同,逻辑覆盖测试可分为:语句覆盖,判定覆盖,判定-条件覆盖,条件组合覆盖及路径覆盖。
基本路径测试是为了解决路径庞大难题,它是在程序控制流程图的基础上,通过
控制构造的环路复杂性,导出基本可执行路径集合,从而设计测试用例的方法。
逻辑覆盖法与基本路径测试法的区别:
逻辑覆盖是通过对程序逻辑结构的遍历实现程序的覆盖,它是一系列测试过程的总称,这组测试过程逐 渐进行越来越完整的通路测试。语句覆盖、判定覆盖、条件覆盖、判定/条件覆盖、条件组合覆盖,以及修正条件/判定覆盖等都是从不同要求出发,为了设计测试 用例提供依据的度量
。
基本路径测试法是在程序控制流图的基础上,通过分析控制结构的环路复杂性,导出基本可执行路径集合,从而设计测试用例的方法。设计出的测试用例要保证在测试中程序的每个可执行语句至少执行一次。
逻辑覆盖主要针对程序中的由于判定条件所产生的分支结构进行测试; 路径覆盖只要从由于各种逻辑判定条件所形成的复杂的程序执行路径这个角度入手;
即便是逻辑覆盖程度最高的条件组合覆盖,也不能保证覆盖了所有的分支组合(条件组合覆盖度量标准只考虑了每个判定内部的条件组合情况),而符合路径覆盖的一组用例,也不能完全保证它一定符合了条件组合覆盖的度量标准。 2.1.1 基本路径测试法
在程序控制流图的基础上,通过分析控制构造的环路复杂性,导出基本可执行路径集合,从而设计测试用例。包括以下4个步骤和一个工具方法:
1. 程序的控制流图:描述程序控制流的一种图示方法。
2. 程序圈复杂度:McCabe复杂性度量。从程序的环路复杂性可导出程序基本路径集合中的独立路径条数,这是确定程序中每个可执行语句至少执行一次所必须的测试用例数目的上界。
3. 导出测试用例:根据圈复杂度和程序结构设计用例数据输入和预期结果。
4. 准备测试用例:确保基本路径集中的每一条路径的执行。
工具方法:
图形矩阵:是在基本路径测试中起辅助作用的软件工具,利用它可以实现自
动地确定一个基本路径集。
程序的控制流图:描述程序控制流的一种图示方法。
在将程序流程图简化成控制流图时,应注意:
在选择或多分支结构中,分支的汇聚处应有一个汇聚结点。
边和结点圈定的区域叫做区域,当对区域计数时,图形外的区域也应记为一个区域。如下图所示
如果判断中的条件表达式是由一个或多个逻辑运算符 (OR, AND, NAND, NOR)
连接的复合条件表达式,则需要改为一系列只有单条件的嵌套的判断。 例如:
if a or b
x
else
y
对应的逻辑为:
1)基本路径测试法的步骤:
在介绍基本路径方法之前,必须先介绍一种简单的控制流表示方法,即流图。流图使用下面的符号描述逻辑控制流,每一种结构化构成元素有一个相应的流图符号。
顺序结构IF选择结构WHILE重复结构UNTIL重复结构CASE多分支结构
例子
如下面的C函数:
void Sort(int iRecordNum,int iType) 1 {
2 int x=0;
3 int y=0;
4 while (iRecordNum-- > 0) 5 {
6 if(0= =iType)
7 x=y+2;
8 else
9 if(1= =iType)
10 x=y+10;
11 else
12 x=y+20;
13 }
14 }
第一步:画出控制流图
c/c++语句中的控制语句表示含义如下:
图中的每一个圆称为流图的结点,代表一条或多条语句。流图中的箭头称为边或连接,代表控制流。
为了说明流图的用法,我们采用过程设计表示法,此处,流程图用来描述程序控制结构。可将流程图映射到一个相应的流图(假设流程图的菱形决定框中不包含复合条件)。在流图中,每一个圆,称为流图的结点,代表一个或多个语句。
一个处理方框序列和一个菱形决测框可被映射为一个结点,流图中的箭头,称为边或连接,代表控制流,类似于流程图中的箭头。一条边必须终止于一个结点,即使该结点并不代表任何语句(例如:参见if-else-then结构的符号)。由边和结点限定的范围称为区域。计算区域时应包括图外部的范围。
任何过程设计都要被翻译成控制流图。
画出其程序流程图和对应的控制流图如下:
4
64
78
6 1011 78
101113
14
1413
程序流程图 控制流图
程序设计中遇到复合条件时,生成的流图变得更为复杂。当条件语句中用到一个或多个布尔运算符(逻辑OR,AND,NAND,NOR)时,就出现了复合条件。下图为语句IF a OR b中的每一个a和b创建了一个独立的结点,包含条件的结点被称为判定结点,从每一个判定结点发出两条或多条边。例如:
1 if a or b
2 x 1a
3 else
4 y
1b 对应的逻辑为:
232
第二步:计算圈复杂度
圈复杂度是一种为程序逻辑复杂性提供定量测度的软件度量,将该度量用于计算
程序的基本的独立路径数目,为确保所有语句至少执行一次的测试数量的上界。独立路径必须包含一条在定义之前不曾用到的边。
有以下三种方法计算圈复杂度:
1. 流图中区域的数量对应于环型的复杂性;
2. 给定流图G的圈复杂度,V(G),定义为V(G)=E-N+2,E是流图中边的数
量,N是流图中结点的数量;
3. 给定流图G的圈复杂度,V(G),定义为V(G)=P+1,P是流图G中判定结
点的数量。
对应上面图中的圈复杂度,计算如下:
4, 流图中有四个区域;
, V(G)=11条边-9结点+2=4;
6, V(G)=3个判定结点+1=4。 3
78
4
1
1011 2
13
14
第三步:导出测试用例
根据上面的计算方法,可得出四个独立的路径:
, 路径1:4-14
, 路径2:4-6-7-14
, 路径3:4-6-8-10-13-4-14
, 路径4:4-6-8-11-13-4-14
根据上面的独立路径,去设计输入数据,使程序分别执行到上面四条路径。
第四步:准备测试用例
为了确保基本路径集中的每一条路径的执行,根据判断结点给出的条件,选择适当的数据以保证某一条路径可以被测试到,满足上面例子基本路径集的测试用例是:
路径1:4-14
输入数据:iRecordNum,0,或者取iRecordNum<0的某一个值 预期结果:x,0
路径2:4-6-7-14
输入数据:iRecordNum,1,iType,0
预期结果:x,2
路径3:4-6-8-10-13-4-14 输入数据:iRecordNum,1,iType,1
预期结果:x,10
路径4:4-6-8-11-13-4-14 输入数据:iRecordNum,1,iType,2
预期结果:x,20
例:下例程序流程图描述了最多输入50个值(以–1作为输入结束标志),计
算其中有效的学生分数的个数、总分数和平均值。
步骤1:导出过程的流图。
步骤2:确定环形复杂性度量V(G):
1)V(G)= 6 (个区域)
2)V(G)=E–N+2=16–12+2=6
其中E为流图中的边数,N为结点数;
3)V(G)=P+1=5+1=6
其中P为谓词结点的个数。在流图中,结点2、3、5、6、9是谓词结点。
步骤3:确定基本路径集合(即独立路径集合)。于是可确定6条独立的路径:
路径1:1-2-9-10-12
路径2:1-2-9-11-12
路径3:1-2-3-9-10-12
路径4:1-2-3-4-5-8-2…
路径5:1-2-3-4-5-6-8-2…
路径6:1-2-3-4-5-6-7-8-2…
步骤4:为每一条独立路径各设计一组测试用例,以便强迫程序沿着该路径至少执行一次。
-2-9-10-12)的测试用例: 1)路径1(1
score[k]=有效分数值,当k < i ;
score=–1, 2?i?50;
期望结果:根据输入的有效分数算出正确的分数个数n1、总分sum和平均分average。
2)路径2(1-2-9-11-12)的测试用例:
score[ 1 ]= – 1 ;
期望的结果:average = – 1 ,其他量保持初值。
-2-3-9-10-12)的测试用例: 3)路径3(1
输入多于50个有效分数,即试图处理51个分数,要求前51个为有效分数;
期望结果:n1=50、且算出正确的总分和平均分。
4)路径4(1-2-3-4-5-8-2…)的测试用例:
score=有效分数,当i<50;
score[k]<0, k< i ;
期望结果:根据输入的有效分数算出正确的分数个数n1、总分sum和平均分average。
5)路径5的测试用例: score=有效分数, 当i<50;
score[k]>100, k< i ;
期望结果:根据输入的有效分数算出正确的分数个数n1、总分sum和平均分average。
6)路径6(1-2-3-4-5-6-7-8-2…)的测试用例:
score=有效分数, 当i<50;
期望结果:根据输入的有效分数算出正确的分数个数n1、总分sum和平均分average。
2)基本路径测试中的图形矩阵工具
o 导出控制流图和决定基本测试路径的过程均需要机械化,为了开发辅助基本路径测试的软件工具,称为图形矩阵(graph matrix)的数据结构很有用。
利用图形矩阵可以实现自动地确定一个基本路径集。一个图形矩阵是一个方阵,其行/列数控制流图中的结点数,每行和每列依次对应到一个被标识的结点,矩 阵元素对应到结点间的连接(即边)。在图中,控制流图的每一个结点都用数字加以标识,每一条边都用字母加以标识。如果在控制流图中第i个结点到第j个结点 有一个名为x的边相连接,则在对应的图形矩阵中第i行/第j列有一个
非空的元素x。
对每个矩阵项加入连接权值(link weight),图矩阵就可以用于在测试中评估程序的控制结构,连接权值为控制流提供了另外的信息。最简单情况下,连接权值是 1(存在连接)或0(不存在连接),但是,连接权值可以赋予更有趣的属性:
执行连接(边)的概率。
穿越连接的处理时间。
穿越连接时所需的内存。
穿越连接时所需的资源。
根据上面的方法对例4画出图形矩阵如下:
连接权为“1”表示存在一个连接,在图中如果一行有两个或更多的元素“1”,则这行所代表的结点一定是一个判定结点,通过连接矩阵中有两个以上(包括两个)元素为“1”的个数,就可以得到确定该图圈复杂度的另一种算法。 2.1.2 逻辑覆盖
逻辑覆盖也是白盒测试中动态测试的主要方法之一,是以程序内部的逻辑结构为基础的测试技术,是通过对程序逻辑结构的遍历实现程序测试的覆盖,这种方法要求测试人员对程序的逻辑结构有清楚的了解。
1逻辑覆盖的类型
语句覆盖:在测试时,首先设计若干个测试用例,然后运行被测程序,使程
序中的每个可执行语句至少执行一次。这时所谓“若干个”,自然是越少越好。
判定覆盖(分支覆盖):设计若干测试用例,运行被侧程序,使得程序
中每个判断的取真分支和取假分支至少经历一次,即判断的真假值均曾被满
足。
条件覆盖:设计若干测试用例,执行被测程序以后,要使每个判断中每个
条件的可能取值至少满足一次。
判定/条件覆盖:要求设计足够的测试用例,使得判断中每个条件的所有可能
至少出现一次,并且每个判断本身的判定结果也至少出现一次。
条件组合覆盖:设计足够多的测试用例,使得判定中的每个条件的所有可能
(真/假)至少出现一次,并且每个判定本身的判定结果也至少出现一次; 这里先给出一张程序流程图。(本文以1995年软件设计师考试的一道考
目为例,图中红色字母代表程序执行路径)。
1、语句覆盖
1)主要特点:语句覆盖是最起码的结构覆盖要求,语句覆盖要求设计足够多的测试用例,使得程序中每条语句至少被执行一次。
2)用例设计:(如果此时将A路径上的语句1—〉T去掉,那么用例如下)
X Y 路径
1 50 50 OBDE
2 90 70 OBCE
3)优点:可以很直观地从源代码得到测试用例,无须细分每条判定表达式。
4)缺点:由于这种测试方法仅仅针对程序逻辑中显式存在的语句,但对于隐藏的条件和可能到达的隐式逻辑分支,是无法测试的。在本例中去掉了语句1—〉T去掉,那么就少了一条测试路径。在if结构中若源代码没有给出else后面的执行分支,那么语句覆盖测试就不会考虑这 种情况。但是我们不能排除这种以外的分支不会被执行,而往往这种错误会经常出现。再如,在Do-While结构中,语句覆盖执行其中某一个条件分支。那么 显然,语句覆盖对于多分支的逻辑运算是无法全面反映的,它只在乎运行一次,而不考虑其他情况。
2、判定覆盖
1)主要特点:判定覆盖又称为分支覆盖,它要求设计足够多的测试用例,使得程序中每个判定至少有一次为真值,有一次为假值,即:程序中的每个分支至少执行一次。每个判断的取真、取假至少执行一次。
2)用例设计:
X Y 路径
1 90 90 OAE
2 50 50 OBDE
3 90 70 OBCE
3)优点:判定覆盖比语句覆盖要多几乎一倍的测试路径,当然也就具有比语句覆盖更强的测试能力。同样判定覆盖也具有和语句覆盖一样的简单性,无须细分每个判定就可以得到测试用例。
4)缺点:往往大部分的判定语句是由多个逻辑条件组合而成(如,判定语句中包含AND、OR、CASE),若仅仅判断其整个最终结果,而忽略每个条件的取值情况,必然会遗漏部分测试路径。
3、条件覆盖
1)主要特点:条件覆盖要求设计足够多的测试用例,使得判定中的每个条件获得各种可能的结果,即每个条件至少有一次为真值,有一次为假值。
2)用例设计:
X Y 路径
1 90 70 OBC
2 40 OBD
3)优点:显然条件覆盖比判定覆盖,增加了对符合判定情况的测试,增加了测试路径。
4)缺点:要达到条件覆盖,需要足够多的测试用例,但条件覆盖并不能保证判定覆盖。条件覆盖只能保证每个条件至少有一次为真,而不考虑所有的判定结果。
4、判定/条件覆盖
1)主要特点:设计足够多的测试用例,使得判定中每个条件的所有可能结果至少出现一次,每个判定本身所有可能结果也至少出现一次。
2)用例设计:
X Y 路径
1 90 90 OAE
2 50 50 OBDE
3 90 70 OBCE
4 70 90 OBCE
3)优点:判定/条件覆盖满足判定覆盖准则和条件覆盖准则,弥补了二者的不足。
4)缺点:判定/条件覆盖准则的缺点是未考虑条件的组合情况。
5、组合覆盖
1)主要特点:要求设计足够多的测试用例,使得每个判定中条件结果的所有可能组合至少出现一次。
2)用例设计:
X Y 路径
1 90 90 OAE
2 90 70 OBCE
3 90 30 OBDE
4 70 90 OBCE
5 30 90 OBDE
6 70 70 OBDE
7 50 50 OBDE
3)优点:多重条件覆盖准则满足判定覆盖、条件覆盖和判定/条件覆盖准则。更改的判定/条件覆盖要求设计足够多的测试用例,使得判定中每个条件的所有可能结果至少出现一次,每个判定本身的所有可能结果也至少出现一次。并且每个条件都显示能单独影响判定结果。
4)缺点:线性地增加了测试用例的数量。
6、路径覆盖
1)主要特点:设计足够的测试用例,覆盖程序中所有可能的路径。
2)用例设计:
X Y 路径
1 90 90 OAE
2 50 50 OBDE
3 90 70 OBCE
4 70 90 OBCE
3)优点:这种测试方法可以对程序进行彻底的测试,比前面五种的覆盖面都广。
4)缺点:由于路径覆盖需要对所有可能的路径进行测试(包括循环、条件组合、分支选择等),那么需要设计大量、复杂的测试用例,使得工作量呈指数级增长。而在有些情况下,一些执行路径是不可能被执行的,如:
If (!A)B++;
If (!A)D--;
这两个语句实际只包括了2条执行路径,即A为真或假时候对B和D的处理,真或假不可能都存在,而路径覆盖测试则认为是包含了真与假的4条执行路径。这样不仅降低了测试效率,而且大量的测试结果的累积,也为排错带来麻烦。 2.1.3 白盒测试应用策略
白盒测试是一种被广泛使用的逻辑测试方法,是由程序内部逻辑驱动的一种单元测试方法。只有对程序内部十分了解才能进 行适度有效的白盒测试。但是贯穿在程序内部的逻辑存在着不确定性和无穷性,尤其对于大规模复杂软件。因此我们不能穷举所有的逻辑路径,即使穷举也未必会带 来好运(穷举不能查出程序逻辑规则错误,不能查出数据相关错误,不能查出程序遗漏的路径)。
那么正确使用白盒测试,就要先从代码分析入手,根据不同的代码逻辑规则、语句执行情况,选用适合的覆盖方法。任何一个高效的测试用例,都是针对具体测试场景的。逻辑测试不是片面的测试正确的结果或是测试错误的结果,而是尽可能全面地覆盖每一个逻辑路径。
在测试中,应该尽量先使用工具进行静态结构分析。
测试中可以采取先静态后动态的组合方式:先进行静态结构分析、代码检查,在进行覆盖率测试
利用静态分析的结果作为引导,通过代码检查和动态测试的方式对静态分析结果做进一步确认,使测试工作更为有效。
覆盖率测试使白盒测试的重点,一般可以使用基本路径测试法达到语句覆盖标准;对于软件的重点模块,应使用多种覆盖率衡量测试的覆盖率
在不同的测试点,测试的侧重点不同:在单元测试阶段,以代码检查、逻辑覆盖为主;在集成测试阶段,需要增加静态结构分析等;在系统测试阶段,应根据黑盒测试的结果,采取相应的白盒测试。