470 likes | 614 Views
白盒测试技术. 王林章 lzwang@nju.edu.cn 18951679130 南京大学计算机科学与技术系. 问题. 下列程序实现如下功能:输入 3 个数作为三角形的 3 条边,判断该 3 条边是否可以构成一个三角形,若可构成一个三角形判断该三角形的具体类型。 triangle(){ int a, int b, int c;//a, b, c 分别为三角形的三条边 string result; input(a,b,c); if (a + b > c && a + c > b && b + c > a) { if (a == b) {
E N D
白盒测试技术 王林章 lzwang@nju.edu.cn 18951679130 南京大学计算机科学与技术系
问题 下列程序实现如下功能:输入3个数作为三角形的3条边,判断该3条边是否可以构成一个三角形,若可构成一个三角形判断该三角形的具体类型。 triangle(){ int a, int b, int c;//a, b, c分别为三角形的三条边 string result; input(a,b,c); if (a + b > c && a + c > b && b + c > a) { if (a == b) { if( b == c) { result:=’等边三角形’} else {result:=’等腰三角形’}} else { if ( b == c ){result:=’等腰三角形’} else { if ( a == c) {result:=’等腰三角形’} else {result:=’一般三角形’ } } else { result:=’不构成三角形’} printf (result) ;//输判定结果} • 如何根据代码设计测试用例,确保其正确性
主要内容 • 概述 • 程序结构分析基础 • 基本路径方法 • 逻辑覆盖
概述 • 此方法把测试对象看做一个透明的盒子,它允许测试人员利用程序内部的逻辑结构及有关信息,设计或选择测试用例,对程序所有逻辑路径进行测试。 • 通过在不同点检查程序的状态,确定实际的状态是否与预期的状态一致。因此白盒测试又称为结构测试或逻辑驱动测试。
概述 • 软件人员使用白盒测试方法,主要想对程序模块进行如下的检查: • 对程序模块的所有独立的执行路径至少测试一次; • 对所有的逻辑判定,取“真”与取“假”的两种情况都至少测试一次; • 在循环的边界和运行界限内执行循环体; • 测试内部数据结构的有效性,等。
概述 • 对一个具有多重选择和循环嵌套的程序,不同的路径数目可能是天文数字。给出一个小程序的流程图,它包括了一个执行20次的循环。 • 包含的不同执行路径数达520条,对每一条路径进行测试需要1毫秒,假定一年工作365 × 24小时,要想把所有路径测试完,需3170年。
程序结构分析 进行静态分析:结构图分析、控制流分析、数据流分析等; • 结构图分析是建立和检查程序中模块(类、函数)之间的相互关系,得到软件系统结构的总貌。 • 控制流分析是检查程序的控制结构,以验证程序结构的一些规则在程序编写过程中是否得到遵循,在此基础上可得到有关构成成分的语法树,并揭示控制结构的缺陷。
程序结构分析(续) • 数据流分析方法用以查找如引用未定义变量等程序错误,也可用来查找对以前未曾使用的变量再次赋值等数据流异常的情况。找出这些错误是很重要的,如拼错名字、名字混淆或是丢失了语句 , 因为这是常见程序错误的表现形式。
控制流图 对程序流程图(flow-chart) 进行简化后的图称为控制流图(control-flow graph)。 在控制流图中只有两种图形符号: ①结点,用带标号的圆圈表示,代表一个或多个语句、一个处理方框序列和一个菱形判断框(假设不包含复合条件)都可以映射为一个结点; ②控制流线,用带箭头的弧或线表示,称为边或两连接。它代表程序的控制流,类似于流程图中的箭头线,控制流线常标有名字。 包含条件的节点被称为判定结点(也叫谓词结点),从每一个判定节点发出两条或多条边。一条边必须终止于一个结点,由边和结点限定的范围称为区域。
程序的控制流图 • 符号○为控制流图的一个结点,表示一个或多个无分支的语句或源程序语句。箭头为边,表示控制流的方向。
程序的控制流图 • 在选择或多分支结构中,分支的汇聚处应有一个汇聚结点。 • 边和结点圈定的区域叫做区域,当对区域计数时,图形外的区域也应记为一个区域。 • 如果判断中的条件表达式是由一个或多个逻辑运算符 (OR, AND, NAND, NOR) 连接的复合条件表达式,则需要改为一系列只有单条件的嵌套的判断。
程序复杂性分析 • 一种程序静态分析方法,它为程序逻辑复杂性提供定量测度的软件度量。 • 以图论为基础的循环度量(Cyclomatic)复杂性为确定需要寻找多少条路径提供了解决思路,是非常有用的软件度量。
循环度量复杂性的计算 可用二种方法之一来计算复杂性: ① 给定流图G的循环度量复杂性——V(G)定义为V(G)=E-N+2,E是边的数量,N是节点的数量; V(G)= E-N+2=11条边-9个节点+2=4 ②给定流图G的循环度量复杂性——V(G)定义为V(G)=P+1,P是流图中判定节点的数量; V(G)= P+1=3个判定节点+1=4 可知,循环度量复杂性为4。
程序基本路径集 • 程序的环路复杂性给出了程序基本路径集中的独立路径条数,这是确保程序中每个可执行语句至少执行一次所必需的测试用例数目的上界。 • 从控制流图来看,一条独立路径是至少包含有一条在其它独立路径中从未有过的边的路径。
程序环路复杂性 • 例如,在图示的控制流图中,一组独立的路径是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组成了控制流图的一个基本路径集。
基本路径方法设计测试用例的过程 • 从代码导出流图 • 确定流图的圈复杂度 • 确定独立路径的基本集 • 导出测试用例,确保基本路径集中的每一条路径的执行。
导出测试用例(续) • 每个测试用例执行之后,与预期结果进行比较。如果所有测试用例都执行完毕,则可以确信程序中所有的可执行语句至少被执行了一次。 • 注意,一些独立的路径(如例中的路径1),往往不是完全孤立的,有时它是程序正常的控制流的一部分,这时,这些路径的测试可以是另一条路径测试的一部分。
实 例 • 介绍用基本路径法设计测试用例。 • 要求:最多输入100个值(以-999为输入结束标志),计算落在给定范围内的那些值(称为有效输入值)的个数、总和及平均值。 • 该程序为主程序调用的一个求平均值的函数average (),sum为总和,total为有效值的个数,二者都为全局变量。主程序main()完成数据的读入(调用时传给value数组)及平均值、总和、有效值个数的输出。
设计测试用例的步骤 ① 画出控制流图,如图所示 ② 确定Cyclomatic复杂性度量V(G); V(G)=17(条边)-13(个节点)+2=6 V(G)=5(个谓词节点)+1=6
设计测试用例的步骤(续) ③ 确定独立路径集合(6条) 路径1:1-2-10-11-13 (aloq) 路径2:1-2-10-12-13 (alnp) 路径3:1-2-3-10-11-13 (abmoq) 路径4:1-2-3-4-5-8-9-2……(abcdfjk……) 路径5:1-2-3-4-5-6-7-8-9-2……(abcdehijk……) 路径6:1-2-3-4-5-6-8-9-2……(abcdegjk……) 在本例中,判定节点(谓词节点)是节点2、3、5、6和节点10。另外要注意路径4、5、6中节点2后面的省略号表示随后的部分已无关紧要。
设计测试用例的步骤(续) ④ 设计测试用例,强制执行独立路径集中的每一条路径。测试人员可选择数据以在测试每条路径时适当设置判定节点的条件。
对应路径的测试用例 路径1的测试用例: Value(k)=有效输入,其中,k<I ; Value(i)=-999,其中2≤i≤100 期望结果:基于k的正确平均值的总数 注意:路径1必须作为路径4、5、6测试的一部分,它无法独立测试。 路径2的测试用例: Value(1)=-999 ; 期望结果:average=-999,其它保持初值 路径3的测试用例: 试图处理101个或更多的值,前100个值应该有效。期望结果与测试用例1相同
测试用例(续) 路径4的测试用例: Value(i)=有效输入,其中,i<100 ; Value(k)<最小值,其中k<i 期望结果:根据有效输入值的个数和总数正确算出平均值 路径5的测试用例: Value(i)=有效输入,其中,i<100 期望结果:根据有效输入值的个数和总数正确算出平均值 路径6的测试用例: Value(i)=有效输入,其中,i<100 ; Value(k)>最大值,其中k<i 期望结果:根据有效输入值的个数和总数正确算出平均值
逻辑覆盖 • 语句覆盖 • 判定覆盖 • 条件覆盖 • 判定-条件覆盖 • 条件组合覆盖 • 路径覆盖。 逻辑覆盖是以程序内部的逻辑结构为基础的设计测试用例的技术。它属白盒测试。
1.7 测试 现给出如下程序: #include(stdio.h); main(){ float A, B, X; scanf(“%f %f %f”, &A, &B, &X); if (A>1)&&(B==0) X=X/A; if (A==2)||(X>1) X=X+1; printf(“%f”, X)} 设计该程序的测试数据以分别满足语句覆盖、判定覆盖、条件覆盖、条件组合覆盖和路径覆概的逻辑覆盖标准。 Software Engineering Group
s 入口 c a X = X / A (A>1)&&(B==0) e b (A==2)||(X>1) X = X + 1 d 返回 1.7 测试 语句覆盖:选取足够多的测试数据,使被测试程序中每个语句至少执行一次。 为使每个语句都执行一次,程序的执行路径应是sacbed: A=2, B=0, X=4 问题:若把b点的判定错写为: (A==2)||(X<1) Software Engineering Group
入口 s c a X = X / A (A>1)&&(B==0) e b (A==2)||(X>1) X = X + 1 d 返回 1.7 测试 判定覆盖:选取足够多的测试数据,使被测试程序中不仅每个语句至少执行一次,而且每个判定的每种可能的结果都至少执行一次。 能够分别覆盖路径sacbed和sabd或sacbd和sabed的两组测试数据,都满足判定覆盖标准: (1)A=3, B=0, X=3 (sacbd) (2)A=2, B=1, X=1 (sabed) Software Engineering Group
入口 s c a X = X / A (A>1)&&(B==0) e b (A==2)||(X>1) X = X + 1 d 返回 1.7 测试 条件覆盖:选取足够多的测试数据,使被测试程序中不仅每个语句至少执行一次,而且每个判定表达式中的每个条件都取到各种可能的结果。 在a点:A>1, A1, B=0, B≠0; 在b点:A=2, A ≠2, X>1, X 1。 (1)A=2, B=0, X=4 (sacbed) (2)A=1, B=1, X=1 (sabd) Software Engineering Group
s 入口 c a X = X / A (A>1)&&(B==0) e b (A==2)||(X>1) X = X + 1 d 返回 1.7 测试 判定/条件覆盖:选取足够多的测试数据,使得判定表达式中的每个条件都取到各种可能的结果,而且每个判定表达式也都取到各种可能的结果。 (1)A=2, B=0, X=4 (sacbed) (2)A=1, B=1, X=1 (sabd) Software Engineering Group
s 入口 c a X = X / A (A>1)&&(B==0) e b (A==2)||(X>1) X = X + 1 d 返回 1.7 测试 条件组合覆盖:选取足够多的测试数据,使得判定表达式中条件的各种可能组合都至少出现一次。 有八种可能的条件组合: (1)A>1, B=0;(2)A>1, B ≠0; (3)A 1, B=0;(4) A 1, B ≠0; (5)A=2, X>1;(6)A=2, X 1; (7) A ≠ 2, X>1;(8)A ≠ 2, X 1。 测试数据: (1)A=2, B=0, X=4 (sacbed, 1,5) (2)A=2, B=1, X=1 (sabed, 2,6) (3)A=1, B=0, X=2 (sabed, 3,7) (4)A=1, B=1, X=1 (sabd, 4,8) Software Engineering Group
s 入口 c a X = X / A (A>1)&&(B==0) e b (A==2)||(X>1) X = X + 1 d 返回 1.7 测试 路径覆盖:选取足够多的测试数据,使得程序的每条可能路径都至少执行一次(若程序图中有环,则每个环至少经过一次)。 测试数据: (1)A=1, B=1, X=1 (sabd) (2)A=1, B=1, X=2 (sabed) (3)A=3, B=0, X=1 (sacbd) (4)A=2, B=0, X=4 (sacbed) Software Engineering Group
条件测试路径选择 • 当程序中判定多于一个时,形成的分支结构可以分为两类:嵌套型分支结构和连锁型分支结构。 • 对于嵌套型分支结构,若有n个判定语句,需要n+1个测试用例; • 对于连锁型分支结构, 若有n个判定语句,需要有2n个测试用例,覆盖它的2n条路径。当n较大时将无法测试。
循环测试路径选择 • 循环分为4种不同类型: • 简单循环 • 连锁循环 • 嵌套循环 • 非结构循环。
(1) 简单循环 ① 零次循环:从循环入口到出口 ② 一次循环:检查循环初始值 ③ 二次循环:检查多次循环 ④ m次循环: 检查在多次循环 ⑤ 最大次数循环、比最大次数多一次、少一次的循环。
(2) 嵌套循环 ① 对最内层循环做简单循环的全部测试。所有其它层的循环变量置为最小值; ② 逐步外推,对其外面一层循环进行测试。测试时保持所有外层循环的循环变量取最小值,所有其它嵌套内层循环的循环变量取“典型”值。 ③ 反复进行,直到所有各层循环测试完毕。 ④ 对全部各层循环同时取最小循环次数,或者同时取最大循环次数
(3)连锁循环 如果各个循环互相独立,则可以用与简单循环相同的方法进行测试。但如果几个循环不是互相独立的,则需要使用测试嵌套循环的办法来处理。
(4) 非结构循环 • 这一类循环应该使用结构化程序设计方法重新设计测试用例。
问题 下列程序实现如下功能:输入3个数作为三角形的3条边,判断该3条边是否可以构成一个三角形,若可构成一个三角形判断该三角形的具体类型。 triangle(){ int a, int b, int c;//a, b, c分别为三角形的三条边 string result; input(a,b,c); if (a + b > c && a + c > b && b + c > a) { if (a == b) { if( b == c) { result:=’等边三角形’} else {result:=’等腰三角形’}} else { if ( b == c ){result:=’等腰三角形’} else { if ( a == c) {result:=’等腰三角形’} else {result:=’一般三角形’ } } else { result:=’不构成三角形’} printf (result) ;//输判定结果} • 要求: • 设计能覆盖基础路径集的测试用例集; • 设计能覆盖其分支的测试用例集