1、第第4章章 白盒测试及其白盒测试及其 实例设计实例设计第第4章章 白盒测试及其实例设计白盒测试及其实例设计4.1 逻辑覆盖测试4.2 路径分析测试4.3 其他白盒测试方法4.4 实例设计小结习题本章概述本章概述 白盒测试是软件测试实践中最为有效和实用的方法之一。白盒测试是基于程序的测试,检测产品的内部结构是否合理以及内部操作是否按规定执行,覆盖测试与路径测试是其两大基本策略。本章重点围绕逻辑覆盖和路径分析展开介绍常见的白盒测试方法,并通过实例说明如何实际运用白盒测试技术。4.1 逻辑覆盖测试逻辑覆盖测试 白盒测试技术的常见方法之一就是覆盖测试,它是利用程序的逻辑结构设计相应的测试用例。测试人员
2、要深入了解被测程序的逻辑结构特点,完全掌握源代码的流程,才能设计出恰当的用例。根据不同的测试要求,覆盖测试可以分为语句覆盖、判断覆盖、条件覆盖、判断/条件覆盖、条件组合覆盖和路径覆盖。下面是一段简单的C语言程序,作为公共程序段来说明五种覆盖测试的各自特点。程序4-1:1If(x100&y500)then2 score=score+13If(x=1000|z5000)then4 score=score+5图4-1 程序流程图l 语句覆盖语句覆盖(Statement Coverage)是指设计若干个测试用例,程序运行时每个可执行语句至少被执行一次。在保证完成要求的情况下,测试用例的数目越少越好。以
3、下是针对公共程序段设计的两个测试用例:Test Case 1:x=2000,y=600,z=6000 Test Case 2:x=900,y=600,z=6000l 判断覆盖判断覆盖(Branch Coverage)是指设计若干个测试用例,执行被测试程序时,程序中每个判断条件的真值分支和假值分支至少被执行一遍。在保证完成要求的情况下,测试用例的数目越少越好。判断覆盖又称为分支覆盖。以下是针对公共程序段设计的两个测试用例:Test Case 1:x=2000,y=600,z=6000 Test Case 3:x=50,y=600,z=2000 l条件覆盖条件覆盖(Condition Covera
4、ge)是指设计若干个测试用例,执行被测试程序时,程序中每个判断条件中的每个判断式的真值和假值至少被执行一遍。测试用例组5:Test Case 6:50,600,6000Test Case 7:2000,200,1000l判断/条件覆盖判断/条件覆盖是指设计若干个测试用例,执行被测试程序时,程序中每个判断条件的真假值分支至少被执行一遍,并且每个判断条件的内部判断式的真假值分支也要被执行一遍。测试用例组6:Test Case 1:x=2000,y=600,z=2000Test Case 6:x=2000,y=200,z=6000Test Case 7:x=2000,y=600,z=2000Test
5、 Case 8:x=50,y=200,z=2000l条件组合覆盖条件组合覆盖是指设计若干个测试用例,执行被测试程序时,程序中每个判断条件的的内部判断式的各种真假组合可能都至少被执行一遍。可见,满足条件组合覆盖的测试用例组一定满足判断覆盖、条件覆盖和判断/条件覆盖。测试用例组7:Test Case 1:x=2000,y=600,z=2000Test Case 6:x=2000,y=200,z=6000Test Case 7:x=2000,y=600,z=2000Test Case 8:x=50,y=200,z=2000l路径覆盖路径覆盖(Path Coverage)要求设计若干测试用例,执行被测
6、试程序时,能够覆盖程序中所有的可能路径。测试用例组8:Test Case 1:x=2000,y=600,z=6000 Test Case 3:x=50,y=600,z=2000Test Case 4:x=2000,y=600,z=2000Test Case 7:x=2000,y=200,z=1000应该注意的是,上面6种覆盖测试方法所引用的公共程序只有短短4行,是一段非常简单的示例代码。然而在实际测试程序中,一个简短的程序,其路径数目是一个庞大的数字。要对其实现路径覆盖测试是很难的。所以,路径覆盖测试是相对的,尽可能把路径数压缩到一个可承受范围。当然,即便对某个简短的程序段做到了路径覆盖测试,
7、也不能保证源代码不存在其他软件问题了。其他的软件测试手段也必要的,它们之间是相辅相成的。没有一个测试方法能够找尽所有软件缺陷,只能说是尽可能多地查找软件缺陷。4.2 路径分析测试路径分析测试4.2.1 控制流图控制流图白盒测试是针对软件产品内部逻辑结构进行测试的,测试人员必须对测试中的软件有深入的理解,包括其内部结构、各单元部分及之间的内在联系,还有程序运行原理等等。因而这是一项庞大并且复杂的工作。为了更加突出程序的内部结构,便于测试人员理解源代码,可以对程序流程图进行简化,生成控制流图(Control Flow Graph)。简化后的控制流图是由节点和控制边组成的。控制流图有以下几个特点:控
8、制流图有以下几个特点:l具有唯一入口节点,即源节点,表示程序段的开始语句;l具有唯一出口节点,即汇节点,表示程序段的结束语句;l节点由带有标号的圆圈表示,表示一个或多个无分支的源程序语句;l控制边由带箭头的直线或弧表示,代表控制流的方向。图4-2 常见的控制流图 程序环路复杂性程序环路复杂性 程序的环路复杂性是一种描述程序逻辑复杂度的标准,该标准运用基本路径方法,给出了程序基本路径集中的独立路径条数,这是确保程序中每个可执行语句至少执行一次所必需的测试用例数目的上界。给定一个控制流图G,设其环形复杂度为V(G),在这里介绍三种常见的计算方法来求解V(G)。(1)V(G)=E-N+2,其中E是控
9、制流图G中边的数量,N是控制流图中节点的数目。(2)V(G)=P+1,其中P是控制流图G中判断节点的数目。(3)V(G)=A,其中A是控制流图G中区域的数目。由边和结点围成的区域叫做区域,当在控制流图中计算区域的数目时,控制流图外的区域也应记为一个区域。4.2.2 独立路径测试独立路径测试 从前面学过的覆盖测试一节中可知,对于一个较为复杂的程序要做到完全的路径覆盖测试是不可能实现的。既然路径覆盖测试无法达到,那么可以对某个程序的所有独立路径进行测试,也就是说检验了程序的每一条语句,从而达到语句覆盖,这种测试方法就是独立路径测试方法。从控制流图来看,一条独立路径是至少包含有一条在其它独立路径中从
10、未有过的边的路径。路径可以用控制流图中的节点序列来表示。例如,在如图4-3所示的控制流图中,一组独立的路径是path1:1-11path2:1-2-3-4-5-10-1-11path3:1-2-3-6-8-9-10-1-11path4:1-2-3-6-7-9-10-1-11 路径 path1,path2,path3,path4组成了控制流图的一个基本路径集。图4-3 控制流图示例独立路径测试的步骤包括3个方面:l 导出程序控制流图l 求出程序环形复杂度l 设计测试用例(Test Case)程序4-2:1main()23 int num1=0,num2=0,score=100;4int i;5
11、char str;6 scanf(“%d,%cn”,&i,&str);7 while(i18路径2:7-9-10-16-7-18路径3:7-9-11-15-16-7-18路径4:7-9-11-13-14-15-16-7-18根据上述4条独立路径,设计了测试用例组9,如表4-9所示。测试用例组9中的4个测试用例作为程序输入数据,能够遍历这4条独立路径。对于源程序中的循环体,测试用例组9中的输入数据使其执行零次或一次。表4-9 测试用例组9 测试用例输入期望输出执行路径istrnum1num2scoreTest Case 15T00100路径1Test Case 24T10100路径2Test C
12、ase 34A00100路径3Test Case 44F0190路径4程序4-3:1if(a or b)2then 3 procedure x4else 5 procedure y;6 对应的控制流图如图4-5所示,程序行1的a,b都是独立的判断节点,还有程序行4也是判断节点,所以共计3个判断节点。图4-5的环形复杂度为V(G)=3+1,其中3是图4-5中判断节点的数目。图4-5 程序4-3的控制流图 4.2.3 Z路径覆盖测试路径覆盖测试 Z路径覆盖是路径覆盖面的一种变体。对于语句较少的简单程序,路径覆盖是具有可行性的。但是对于源代码很多的复杂程序,或者对于含有较多条件语句和较多循环体的程序
13、来说,需要测试的路径数目会成倍增长,达到一个巨大数字,以至于无法实现路径覆盖。为了解决这一问题,采用简化循环方法的路径覆盖就是Z路径覆盖。所谓简化循环就是减少循环的次数。不考虑循环体的形式和复杂度如何,也不考虑循环体实际上需要执行多少次,只考虑通过循环体零次和一次这两种情况。这里的零次循环是指跳过循环体,从循环体的入口直接到循环体的出口。通过一次循环体是指检查循环初始值。如图4-6(a)和图4-6(b)所示表示了两种最典型的循环控制结构。图4-6(a)是先比较循环条件后执行循环体,循环体B可能执行也可能不被执行。限定循环体B执行零次和一次,这样就和图4-6(c)的条件结构一样了。图4-6(b)
14、是先执行循环体后比较循环条件。假设循环体B被执行一次,在经过条件判断跳出循环,那么其效果就和图4-6(c)的条件结构只执行右分支的效果一样了。一旦将循环结构简化为选择结构后,路径的数量将大大减少,这样就可以实现路径覆盖测试了。对于实现简化循环的程序,可以将程序用路径树来表示。当得到某一程序的路径树后,从其根节点开始,一次遍历,再回到根节点时,将所经历的叶节点名排列起来,就得到一个路径。如果已经遍历了所有叶子节点,那就得到了所有的路径。当得到所有的路径后,生成每个路径的测试用例,就可以实现Z路径覆盖测试。图4-6 循环结构和条件结构4.3 其他白盒测试方法其他白盒测试方法 4.3.1 循环测试循
15、环测试循环测试是一种着重循环结构有效性测试的白盒测试方法。循环结构测试用例的设计有以下4种模式,如图4-7所示。图4-7 循环测试的模式4.3.2 变异测试变异测试 变异测试是一种故障驱动测试,即针对某一类特定程序故障进行的测试,变异测试也是一种比较成熟的排错性测试方法。它可以通过检验测试数据集的排错能力来判断软件测试的充分性。假设对程序P进行一些微小改动而得到程序MP,程序MP就是程序P的一个变异体。假设程序P在测试集T上是正确的,设计某一变异体集合:M=MP|MP是P的变异体,若变异体集合M中的每一个元素在T上都存在错误,则认为源程序P的正确度较高,否则若M中的某些元素在T上运行正确,则可
16、能存在以下一些情况:M中的这些变异体在功能上与源程序P是等价的;现有的测试数据不足以找出源程序P与其变异体之间的差别;源程序P可能产生故障,而其某些变异体却是正确的。可见,测试集T和变异体集合M中的每一个变异体MP的选择都是很重要的,它们会直接影响变异测试的测试效果。总之,对程序进行变换的方法多种多样,具体操作要靠测试人员的实际经验。通过变异分析构造测试数据集的过程是一个循环过程,当对源程序及其变异体进行测试后,若发现某些变异体并不理想,就要适当增加测试数据,直到所有变异体达到理想状态,即变异体集合中的每一个变异体在T上都存在错误。4.3.3 程序插装程序插装 程序插装是借助于在被测程序中设置
17、断点或打印语句来进行测试的方法,在执行测试的过程中可以了解一些程序的动态信息。这样在运行程序时,既能检验测试的结果数据,又能借助插入语句给出的信息掌握程序的动态运行特性,从而把程序执行过程中所发生的重要事件记录下来。程序插装设计时主要需要考虑三方面因素:(1)需要探测哪些信息;(2)在程序的什么位置设立插装点;(3)计划设置多少个插装点。插装技术在软件测试中主要有以下几个应用:l覆盖分析:程序插装可以估计程序控制流图中被覆盖的程度,确定测试执行的充分性,从而设计更好的测试用例,提高测试覆盖率。l监控在程序的特定位置设立插装点,插入用于记录动态特性的语句,用来监控程序运行时的某些特性,从而排除软
18、件故障。l查找数据流异常程序插装可以记录在程序执行中某些变量值的变化情况和变化范围。掌握了数据变量的取值状况,就能准确地判断是否发生数据流异常。虽然数据流异常可以用静态分析器来发现,但是使用插装技术可以更经济更简便,毕竟所有信息的获取是随着测试过程附带得到的。4.4 实例设计实例设计 实例1运用逻辑覆盖的方法测试程序程序4-4:1If(x1&y1)then2 z=z*23If(x=3|z1)then4 y+;运用逻辑覆盖的方法设计测试用例组,如表4-10所示。实例2 运用路径分析的方法测试程序程序4-5:1main()23 int flag,t1,t2,a=0,b=0;4 scanf(“%d,
19、%d,%dn”,&flag,&t1,&t2);5 while(flag0)6 7 a=a+1;8 if(t1=1)9 then 10 11 b=b+1;12 flag=0;13 14 else 15 16 if(t2=1)17 then b=b-1;18 else a=a-2;19 flag-;20 2122 printf(“a=%d,b=d%n”,a,b);23 1.程序的流程图如图4-8所示:图4-8 程序4-5的流程图 2.程序的控制流图如图4-9所示,其中R1、R2、R3和R4代表控制流图的4个区域。R4代表的是控制流图外的区域,也算作控制流图的一个区域。图4-9 程序4-5的控制流图
20、 3.运用路径分析的方法设计测试用例组。(1)根据程序环形复杂度的计算公式,求出程序路径集合中的独立路径数目。公式1:V(G)=11-9+2,其中10是控制流图G中边的数量,8是控制流图中节点的数目。公式2:V(G)=3+1,其中3是控制流图G中判断节点的数目。公式3:V(G)=4,其中4是控制流图G中区域的数目。因此,控制流图G的环形复杂度是4。(2)根据上面环形复杂度的计算结果,源程序的基本路径集合中有4条独立路径:路径1:5-22路径2:5-7,8-11,12-21-5-22路径3:5-7,8-16-17-19-21-5-22路径4:5-7,8-16-18-19-21-5-22(3)设计
21、测试用例组11如表4-11所示。根据上述4条独立路径设计出了这组测试用例,其中的4组数据能够遍历各个独立路径,也就满足了路径分析测试的要求。需要注意的是,对于源程序中的循环体,测试用例组11中的输入数据使其执行零次或一次。表4-11 测试用例组11 小结 白盒测试是基于被测程序的源代码设计测试用例的测试方法。常见的白盒测试方法有逻辑覆盖测试和路径分析测试两大类。在逻辑覆盖测试中,按照覆盖策略由弱到强的严格程度,介绍了语句覆盖、判断覆盖、条件覆盖、判断/条件覆盖、条件组合覆盖和路径覆盖六种覆盖测策略。l 语句覆盖:每个语句至少执行一次。l 判定覆盖:在语句覆盖的基础上,每个判定的每个分支至少执行
22、一次。l 条件覆盖:在语句覆盖的基础上,使每个判定表达式的每个条件都取到各种可能的结果。l 判定/条件覆盖:即判定覆盖和条件覆盖的交集。l 条件组合覆盖:每个判定表达式中条件的各种可能组合都至少出现一次。l 路径覆盖:每条可能的路径都至少执行一次,若图中有环,则每个环至少经过一次。在路径分析测试中,介绍了独立路径测试和Z路径覆盖测试两种常用方法。l 独立路径测试方法把覆盖的路径数压缩到一定限度内,程序中的循环体最多只执行一次,对程序中所有独立路径进行测试。它是在程序控制流图的基础上,分析控制构造的环路复杂性,导出基本可执行路径集合,设计测试用例的方法。设计出的测试用例要保证程序的每一个可执行语
23、句至少要执行一次。l Z路径覆盖测试是指采用简化循环的方法进行路径覆盖测试。被测源程序中的循环体执行零次或一次。最后,介绍了其他一些白盒测试方法。循环测试是一种着重循环结构有效性测试的测试方法。变异测试是一种故障驱动测试,针对某一类特定程序故障进行的测试。程序插装是借助于在被测程序中设置断点或打印语句来进行测试的方法,在执行测试的过程中可以了解一些程序的动态信息。习题 1.阐述白盒测试的各种方法,进行分析总结。2.分析归纳逻辑覆盖测试的6种覆盖策略的各自特点。3.简述独立路径测试的基本步骤。4.对下列C语言程序设计逻辑覆盖测试用例。Void test(int X,int A,int B)If(A1&B0)then X=X/AIf(A=2|X1)then X=X+1;