297.白盒测试
1.概述
1.1 白盒测试基本概念
白盒测试又称为结构测试或逻辑驱动测试,是针对被测试程序单元内部如何工作的测试,特点是基于被测试程序的源代码,而不是软件的需求规格说明。(以代码为对象)
使用白盒测试方法时,测试者必须全面了解程序内部逻辑结构,检查程序的内部结构,从检查程序的逻辑着手,对相关的逻辑路径进行测试,最后得出测试结果。
1.2采用白盒测试方法必须遵循原则
(1)保证一个模块中的所有独立路径至少被测试一次。
(2)所有逻辑值均需测试真值和假值两种情况。
(3)检查程序的内部数据结构,保证其结构的有效性。
(4)在上下边界及可操作范围内运行所有循环。
2.静态白盒测试方法
静态白盒测试主要通过审查、走查、检验等方法,来查找代码中的问题和缺陷。
主要原因是为了尽早发现软件缺陷,以找出黑盒测试难以发现或隔离的软件缺陷。其次,为黑盒测试员在接受软件进行测试设计时,设计和应用测试用例提供思路。通过审查评论,可以确定有问题或者容易产生软件缺陷的特性范围。
2.1检查设计和代码
静态白盒测试是在不执行软件的条件下有条理地仔细审查软件设计、体系结构和代码,从而找出软件缺陷的过程。有时又称为结构化分析。
2.2正式审查
1、正式审查有四个要素
- (1)确定问题
- (2)遵守规则
- (3)准备
- (4)编写报告
2、正式审查的效果
正式审查的主要的目的是找出软件中存在的缺陷,除此之外,还可以形成一些间接的效果。如:程序员与程序、测试人员之间的交流,增强相互了解;程序员会更仔细的编程,提高正确率等。正式审查是把大家聚在一起讨论同一个项目问题的良机。
3、正式审查几种类型
- (1)同事审查
- (2)走查
- (3)检验
4.编码标准和规范(保证审查进行的前提)
在编程和审查程序代码时,建立相关的规范和标准,并坚持标准或规范。三个重要的原因:
- (1)可靠性:坚持按照某种标准和规范编写的代码更加可靠和安全。
- (2)可读性/维护性:符合设备标准和规范的代码易于阅读、理解和维护。
- (3)移植性:代码符合设备标准,迁移到另一个平台就会轻而易举,甚至完全没有障碍。
1、编程标准和规范示例
编程标准的4个组成部分
- ①标题:描述标准包含的主题。
- ②标准(或规范):描述标准或规范的内容。
- ③解释说明:给出标准背后的原因,以使程序员理解为什么这样是好的编程习惯。
- ④示例:给出如何使用标准的简单程序示例。
示例如图3-1所示,是一个针对C++中所用的C语言特性的规范示例。说明在C++中如何使用某些C语言特性的编程。
2、获取标准
国际标准化组织(ISO): www.iso.ch
电子电气工程学会(IEEE):www.ieee.org
美国国家标准学会(ANSI):www.ansi.org
国际工程协会(IEC):www.iec.org
信息技术标准国家委员会(NCITS):www.ncits.org
美国计算机协会(ACM):www.acm.org
5.通用代码审查清单
1、数据引用错误
数据引用错误是指使用未经正确声明和初始化的变量、常量、数组、字符串或记录而导致的软件缺陷。数据引用错误是缓冲区溢出的主要原因。
2、数据声明错误
数据声明缺陷产生的原因是不正确地声明或使用变量和常量。
3、计算错误
计算或运算错误就是计算无法得到预期的结果。
4、比较错误
在使用比较和判断运算时产生的比较和判断错误,这种错误很可能是因为边界条件问题。
5、控制流程错误
控制流程错误
产生的原因是编程语言中循环等控制结构未按预期的方式工作。通常由计算或者比较错误直接或间接造成。
6、子程序参数错误子程序参数错误的来源是软件子程序不正确地传递数据。
7、输入/输出错误
输入输出错误包括文件读取、接受键盘或鼠标输入,以及向打印机或屏幕等输出设备写入错误。
8、其他检查
6.程序复杂度及度量方法
在实际的软件开发过程中,人们发现程序的复杂度不仅影响软件的可维护性、可测试性及可靠性等,而且与软件中故障的数量、软件的开发成本及软件的效率有关。
6.1流图的概念
流图又称程序图,实际上可以看作是一种简化了的程序流程图。在流图中,只关注程序的流程,不关心各个处理框的细节,因此,原来程序流程图中的各个处理框(包括语句框、判断框、输入/输出框等)都被简化为结点,一般用圆圈表示,而原来程序流程图中的带有箭头的控制流变成了程序图中的有向边。
结构化程序设计中的几种基本结构的流图。如图3-2所示。
简化后的流图只有两种图形符号:结点和控制流线。结点用带标号的圆圈表示,可以代表一个或多个语句、一个处理框或一个判断框。控制流线用带箭头的弧线表示,代表程序中控制流。
从图论的观点来看,流图是一个可表示为G=<N,E>的有向图。其中,N表示图中的结点,而E表示图中的有向边。
流图可以通过简化程序流程图得到,也可以由PAD图或其他详细设计表达工具变换得到。
图3-3是典型的程序流程图转换为相对应的流图。对图3-3中的(a)所示的程序流程图进行简化,得到图3-3(b)所示的流图。
图3-3程序流程图及对应的流图(a)程序流程图;(b)流图
6.2 环形复杂度
环形复杂度又称为圈复杂度,是一种为程序逻辑复杂度提供定量尺度的软件度量。它可以提供程序基本集的独立路径数量和确保所有语句至少执行一次的过程。常用于基本路径测试法。
环形复杂度的度量方法又称为McCabe方法。一个强连通流图中线性无关的有向环的个数就是该程序的环形复杂度。而强连通图,是指从图中任意一个结点出发都能到达图中其他结点的有向图。
在图论中可以通过以下公式来计算有向图中线性无关的有向环的个数。
$ V(G)=m-n+p ① $
其中:V(G)表示有向图G中的线性无关的环数;
m表示有向图G中有向边的个数;
n表示有向图中的结点数;
p表示有向图G中可分离出的独立连通区域数,为常数1。
流图虽为连通图,但不是强连通图,可以在流图中增加一条出口点到入口点的虚弧线,此时,流图就变成了一个强连通图。如图3-4所示,在图3-3(b)流图添加虚弧后得到的强连通图。
图3-4将图3-3(b)变换后的强连通图
采用上面的公式①计算它的环形复杂度为:
$V(G)=13-10+1=4 $ (加一是考虑虚弧)
图3-4强连通图的复杂度是4,因此图3-4中有4个线性独立环路。此时删除从结点E到结点S的虚弧,则这4个环路就是结点S到结点E的线性独立路径。
4条线性独立路径:
Path1: S→a→b→g→E
Path2: S→a→b→g→h→E
Path3: S→a→b→c→d→f→b→g→E
Path4: S→a→b→c→e→f→b→g→E
除了采用上面的公式①可以计算环形复杂度外,还可以用其他的公式计算出流图中的环形复杂度。
\(V(G)=强连通的流图在平面上围成的区域数\) ②
图3-4中,流图中围成的区域有(b,c,d,f,b),(c,d,f,e,c),(g,h,E,g)和(S,a,b,g,E,S),因此公式②计算得到的流图环形复杂度为4。
\(V(G)=判定结点数+1\) (判定节点为出来弧>1)③
在图3-4中,判定结点分别为b,c和g,根据公式③可得环形复杂度为:3+1=4。
6.3图矩阵
图矩阵是流图的邻接矩阵的表示形式,其阶数等于流图的结点数,矩阵的每列与每行都对应于标识的某一结点,矩阵元素对应于结点之间的存在的边;有边取值为1,否则为0或不填。
如图3-5和图3-6所示,一个简单流图及对应的邻接矩阵:
行相加判断是否是判定结点
3.动态白盒测试方法
3.1定义
动态白盒测试主要是按一定步骤和方法生成测试用例,并驱动相关模块去执行程序并发现软件中的错误和缺陷。测试人员要求对被测系统内的程序结构有深入的认识,清楚程序的结构、各个组成部分及其之间的关联,以及其内部的运行原理、逻辑等。
内容包括的4部分:
(1)直接测试底层函数、过程、子程序和库。
(2)以完整程序的方式从顶层测试软件,有时根据对软件运行的了解调整测试用例。
(3)从软件获得读取变量和状态信息的访问权,以便确定测试结果与预期结果是否相符,同时强制软件以正常测试难以实现的方式运行。
(4)估算执行测试时“命中”的代码量和具体代码,然后调整测试,去掉多余的测试用例,补充遗漏的测试用例。
3.2逻辑覆盖法
逻辑覆盖法是动态白盒测试中常用的测试技术,是一系列测试过程的总称。有选择地执行程序中的某些最具有代表性的通路来对尽穷测试的唯一可行的替代方法。
逻辑覆盖法的覆盖率是程序中一组被测试用例执行到的百分比。
根据测试覆盖的目标不同,以及覆盖的程度不同,可由弱到强分为:语句覆盖、判定覆盖、条件覆盖、判定/条件覆盖、修正的判定/条件覆盖、条件组合覆盖、路径覆盖。
3.2.1语句覆盖和块覆盖
语句覆盖又称为代码行覆盖,指选择足够多的测试用例,使得程序中的每一条可执行语句至少被执行一次。
程序的基本块就是一个连续的语句序列,只有一个入口点和一个出口点。这些唯一的入口点和出口点就是基本块的第一条语句和最后一条语句。程序的控制总是从基本块的入口点进入,从出口点退出。除了其出口点,程序不可能在基本块的其他任意点退出或中止。
例3-1 下面以一个简单的小程序段来说明怎样设计测试用例。
Void testexample1(int x,int y,int z)
{
if (x>1)&&(y==0)
z=z+x;
if (x==2)||(z>1)
z=z+y;
return z;
}
对于这段testexample1函数相对应的程序控制流程图见图3-7所示。
图3-7例3-1的模块的流程图(图中数字1,2,3,4,5,6,7为边)
对于testexample1函数,完全语句覆盖是从第1行执行到最后一行。因此它的测试用例的设计见表3-1:
表3-1 testexample1语句覆盖测试用例
ID | 输入数据 | 输入数据 | 输入数据 | 返回值 | 通过的路径 |
---|---|---|---|---|---|
ID | x | y | z | z | |
TE1-001 | 2 | 0 | 4 | 6 | 1-4-5-6-7 |
对于testexample1函数,其函数体可以分为五个块,第一块为第一个if语句;第二块为赋值语句z=z+x;第三块为第二个if语句;第四块是赋值语句z=z+y;第五块是 return z语句。将图3-7换为图3-8所示。
图3-8 testexample1函数体的控制流图
块覆盖的测试用例的设计是要将这五块都要遍历,表3-1语句覆盖的测试用例也就是这个函数的块覆盖的测试用例。注意,语句覆盖是覆盖所测试程序段中的所有语句,块覆盖是测试程序段中的所有基本块。
2、判定覆盖
判定覆盖又叫分支覆盖,即设计若干测试用例,使得程序中的每个判定表达式的每种可能的结果值都应该至少执行一次,也就是说每个判定的“真”值分支和“假”值分支都至少执行一次。
例3-2 对于testexample1函数实现判定覆盖设计的测试用例见表3-2:
表3-2 testexample1判定覆盖测试用例
|| ID | 输入数据 | 返回值 | 通过的路径 | | |
| ------- | -------- | ------ | ---------- | ---- | --------- |
| x | y | z | z | | |
| TE1-002 | 2 | 0 | 4 | 6 | 1-4-5-6-7 |
| TE1-003 | 3 | 1 | 1 | 1 | 1-2-3 |
3、条件覆盖
条件覆盖是将各分支的条件考虑在内,即设计足够多的测试用例,不仅每个语句至少执行一次,而且使判定表达式中的每个条件都取到各种可能的结果。也就是说,每个判定中的条件取“真”值和“假”值都需执行一次。
例3-3 对于testexample1函数实现判定覆盖设计的测试用例时要考虑到二个判定中的每一个条件:
第一个判定中的两个条件:条件1:x>1,条件2:y= =0考虑这两个条件分别取“真”和取“假”的情况,则有下面的几种结果出现: x>1,x<=1,y=0,y≠0第二个判定中的两个条件:条件3:x==2,条件4:z>1考虑这两个条件分别取“真”和取“假”的情况,则有下面的几种结果出现: x=2,x≠2,z>1,z<=1因此,要满足上面4个条件的8种结果,设计的测试用例见表3-3所示:
表3-3 testexample1条件覆盖测试用例
ID | 输入数据 | 返回值 | 通过的路径 | ||
---|---|---|---|---|---|
x | y | z | z | ||
TE1-004 | 2 | 0 | 4 | 6 | 1-4-5-6-7 |
TE1-005 | 1 | 1 | 1 | 1 | 1-2-3 |
从表3-3中可知,两个测试用例TE-004、 TE-005就覆盖了所有的条件结果,同时也做到了判定覆盖,但是测试用例有时满足条件覆盖并不满足判定覆盖。
4、判定/条件覆盖 判定/条件覆盖是设计足够多的测试用例,使得判定表达式中的每一个条件都取到各种可能的值,同时每个判定表达式也都取到各种可能的结果。例3-4 对于testexample1函数实现判定/条件覆盖设计的测试用例见表3-3。
5、条件组合覆盖 条件组合覆盖要求设计足够多的测试用例,使得每个判定表达式中的条件的各种组合可能都至少被执行一次。
例3-5 对于testexample1函数的两个判定,存在的所有条件组合有8种: (1)x>1,y=0 (2)x>1,y≠0 (3)x<=1,y=0 (4)x<=1,y≠0 (5)x=2,z>1 (6)x=2,z<=1 (7)x≠2,z>1 (8)x≠2,z<=1根据满足所有的条件组合来设计测试用例,见表3-4。
表3-4 testexample1条件组合覆盖测试用例
ID | 输入数据 | 返回值 | 通过的路径 | ||
---|---|---|---|---|---|
x | y | z | z | ||
TE1-006 | 2 | 0 | 4 | 6 | 1-4-5-6-7 |
TE1-007 | 1 | 1 | 1 | 1 | 1-2-3 |
TE1-008 | 2 | 1 | 1 | 2 | 1-2-6-7 |
TE1-009 | 1 | 0 | 2 | 2 | 1-2-3 |
由表3-4可知,其中TE-006测试用例覆盖了条件组合中的(1)(5),TE-007测试用例覆盖了条件组合中的(4)(8),TE-008测试用例覆盖了条件组合中的(2)(6),TE-009测试用例覆盖了条件组合中的(3)(7)。
6、改进的判定/条件覆盖
一个基于改进的条件/判定覆盖概念的充分性准则又称为MC/DC覆盖,它可以对所有条件和判定进行完全且合理的测试。
满足MC/DC覆盖的测试用例要求如下:
-
(1)每一个基本块都被覆盖了。
-
(2)每一个简单条件都取过真值和假值。
-
(3)每一个判定都得出过所有可能的输出结果。
-
(4)每一个简单条件对表达式的输出结果的影响是独立的。
例3-6 考虑复合条件表达式(A and B) or C,其中A、B、C为简单条件。为了获得MC充分性,必须设计一个测试集来说明每一个简单条件都是独立地影响表达式的输出结果的。 为了构造这样的测试集,固定表达式中三个简单条件的任意两个,变化第三个,见表3-5所示。其中T表示真值,F表示假值。
测试用例号 | 输入 | 表达式值 | 注释 | ||
---|---|---|---|---|---|
A | B | C | |||
1 | T | T | T | T | 固定A、B, A、B为T |
2 | T | T | F | T | |
3 | T | F | T | T | 固定A、B, A为T、B为F |
4 | T | F | F | F | |
5 | F | T | T | T | 固定A、B, A为F、B为T |
6 | F | T | F | F | |
7 | F | F | T | T | 固定A、B, A、B为F |
8 | F | F | F | F | |
9 | T | T | T | T | 固定A、C, A、C为T |
10 | T | F | T | T | |
11 | T | T | F | T | 固定A、C, A为T、C为F |
12 | T | F | F | F | |
13 | F | T | T | T | 固定A、C, A为F、C为T |
14 | F | F | T | T | |
15 | F | T | F | F | 固定A、C, A、C为F |
16 | F | F | F | F | |
17 | T | T | T | T | 固定B、C, B、C为T |
18 | F | T | T | T | |
19 | T | T | F | T | 固定B、C, B为T、C为F |
20 | F | T | F | F | |
21 | T | F | T | T | 固定B、C, B为F、C为T |
22 | F | F | T | T | |
23 | T | F | F | F | 固定B、C, B、C为F |
24 | F | F | F | F |
从表3-5中可以发现,其中有很多是行是重复的。针对三个条件中的每一个条件,选择其中两个测试用例来说明该简单条件对表达式结果的独立影响:对C,选择测试用例(3,4);对B,选择测试用例(11,12);对A,选择测试用例(19,20)。如表3-6所示,共有6个测试用例。
表3-6表达式(A and B) or C 的充分测试集
测试用例号 | 输入 | 表达式值 | 对表达值有影响的输入 | ||
---|---|---|---|---|---|
A | B | C | |||
1 [3] | T | F | T | T | C |
2 [4] | T | F | F | F | |
3 [11] | T | T | F | T | B |
4 [12] | T | F | F | F | |
5 [19] | T | T | F | T | A |
6 [20] | F | T | F | F |
将表3-6再进行简化得表3-7,得其最小测试集如表3-7。可知:复合条件中的每一个简单条件都独立地影响复合条件的结果。程序中每个复合条件都必须测试到。并且此覆盖比组合条件所需的测试用例数要少。对表达式(A and B) or C,在条件组合覆盖下最多需要8个测试用例,而满足MC/DC覆盖只需要4个测试用例即可。
表3-7表达式(A and B) or C 的最小充分测试集
测试用例号 | 输入 | 表达式值 | 对表达值有影响的输入 | ||
---|---|---|---|---|---|
A | B | C | |||
1 | T | F | T | T | 测试用例1、2覆盖C,测试用例2、3覆盖B,测试用例3、4覆盖A |
2 | T | F | F | F | |
3 | T | T | F | T | |
4 | F | T | F | F |
例3-7 对于testexample1函数实现MC/DC覆盖设计测试用例如表3-8、表3-9、表3-10所示。
表3-8 testexample1函数表达式(x>1)&&(y==0)最小充分测试用例集
测试用例号 | 输入 | 表达式值 | 对表达值有影响的输入 | |
---|---|---|---|---|
x | y | |||
1 | 2 | 0 | T | 测试用例1、2覆盖y,测试用例2、3覆盖x |
2 | 2 | 1 | F | |
3 | 1 | 0 | F |
表3-9 testexample1函数表达式(x==2)||(z>1)最小充分测试用例集
测试用例号 | 输入 | 表达式值 | 对表达值有影响的输入 | |
---|---|---|---|---|
x | z | |||
5 | 2 | 0 | T | 测试用例5、6覆盖x,测试用例6、7覆盖z |
6 | 1 | 2 | T | |
7 | 1 | 0 | F |
表3-10 testexample1函数的MC/DC覆盖设计测试用例
ID | 输入数据 | 返回值 | 对表达式有影响的输入 | ||
---|---|---|---|---|---|
x | y | z | z | ||
TE1-010 | 2 | 0 | 1 | 3 | TE-010、 TE-011覆盖y TE-011、 TE-012覆盖x TE-012、 TE-013覆盖z |
TE1-011 | 2 | 1 | 1 | 2 | |
TE1-012 | 1 | 0 | 2 | 2 | |
TE1-013 | 1 | 0 | 0 | 0 |
7、路径覆盖
路径覆盖是指设计足够多的测试用例,使得程序中的所有可能的路径都至少被执行一次。例3-8 对于testexample1函数程序段,满足路径覆盖的测试用例设计如表3-11所示。
表3-11 testexample1路径覆盖测试用例
ID | 输入数据 | 返回值 | 通过的路径 | ||
---|---|---|---|---|---|
x | y | z | z | ||
TE1-014 | 2 | 0 | 4 | 6 | 1-4-5-6-7 |
TE1-015 | 1 | 1 | 1 | 1 | 1-2-3 |
TE1-016 | 2 | 1 | 1 | 2 | 1-2-6-7 |
TE1-017 | 3 | 0 | 0 | 3 | 1-4-5-3 |
当程序中的每一条路径都受到检验,才能使程序受到较全面的检验。由表3-11可知,这段程序非常简单,只有4条路径,但在实际问题中,一个不太复杂的程序中的路径都可能是一个庞大的数字,要在测试中覆盖所有的路径是不可能实现的。为了解决这一难题,常将覆盖的路径数压缩到一定的限度内,如程序中的循环体只执行一次的情况。
8、线性代码序列和跳转覆盖(LCSAJ)
线性代码序列和跳转(Linear Code Sequence And Jump)是一个程序单元,它由一段有序的代码序列组成,该序列结束时会跳转到另一个代码序列开始。
一个LCSAL包含一条或多条语句,表示成三元组(X,Y,Z),其中X、Y分别表示代码序列的第一条语句和最后一条语句,Z是语句Y要跳转到的位置。
当程序的控制到达X时,顺序执行相关语句后到达Y,然后跳转到Z。这样,就称LCSAJ(X,Y,Z)被遍历了,也称被覆盖了。
例3-9 函数testexample2的函数体只有一个条件语句。
1 Void testexample2(int x;inty)
2 {
3 int p;
4 if (x<y)
5 p=y;
6 else
7 p=x;
8 return (p);
9 }
对于这个函数体中的LCSAJ见下表3-12所示。
表3-12 testexample2函数体中的LCSAJ
LCSAJ | 开始行号 | 结束行号 | 跳转到 |
---|---|---|---|
1 | 3 | 6 | return |
2 | 3 | 4 | 7 |
3 | 7 | 8 | return |
根据表3-12中的测试用例使得表3-13中的三个LCSAJ各被遍历了至少一次。
表3-13 testexample2 LCSAJ覆盖测试用例
ID | 输入数据 | 返回值 | LCSAJ | |
---|---|---|---|---|
x | y | p | ||
TE2-001 | 2 | 8 | 8 | 1 |
TE2-002 | 6 | 3 | 6 | 2,3 |
3.4.2 基本路径法
基本路径测试是T.McCabe(音译:麦凯伯)首先提出的一种白盒测试技术。所谓基本路径是指程序中至少引进一条新的语句或一个新的条件的任一路径。
基本路径测试法又称独立路径测试,是在程序控制流图的基础上,通过分析控制结构的环路复杂性,导出基本可执行路径集合,从而设计出相应的测试用例的方法。
基本路径测试的基本步骤为:
(1)根据程序设计结果导出程序流程图的控制流图;
(2)计算程序的环路复杂度;
(3)导出基本路径集,确定程序的独立路径;
(4)根据独立路径,设计相应的测试用例。
例3-10 对于testexample1函数程序段,首先将其控制流图3-7修改成判定框单一条件,见图3-9所示。然后导出图3-9对应的流图,见图3-10所示。
第二步,计算程序环路复杂度。计算环形复杂度可以直接计算程序流程图中判定数量(每个判定是单一条件的),然后加1即可得到。从图3-9可以有4个判定框,所以环路复杂度为5。利用公式:V(G)=e-n+1计算,其中e为图G中的边数,n为图G中的结中数 (注意:要变成强连通图,要增加一条从出口到入口的边)。 V(G)=13-9+1=5。按区域数计算环形复杂数,图3-10中有4个小区域,加上图形外一个区域,共有5个,因此环形复杂度为5。
也可采用图形矩阵方法来计算。见图3-11所示,根据图形矩阵图各点之间的连接权数减1后的和,再加1得到环形复杂数为5。
第三步,确定独立路径集。因为环形复杂数为5,所以有5条基本路径 : path1:1—2—4—5—6—7 path2:1—2—3—4—5—6—7 path3:1—2—3—8—4—5—6—7 path4:1—2—3—8—4—5—9—7 path5:1—2—3—8—4—5—6—9—7
第四步,设计测试用例。根据前面确定的独立路径集,设计测试用例及其输出。见表3-14。
表3-14 testexample1基本路径覆盖测试用例
ID | 输入数据 | 返回值 | 通过的路径 | ||
---|---|---|---|---|---|
x | y | z | z | ||
TE1-014 | 1 | 0 | 1 | 1 | Path1 |
TE1-015 | 3 | 1 | 1 | 1 | Path2 |
TE1-016 | 3 | 0 | 3 | 6 | Path3 |
TE1-017 | 2 | 0 | 2 | 4 | Path4 |
TE1-018 | 3 | 0 | 6 | 9 | Path5 |
条件测试
BRO测试
3.4.3 循环测试
循环测试是一种白盒测试技术,专注于测试循环结构的有效性。它遵循的基本测试原则是:在循环的边界和运行界限执行循环体。 在结构化的程序中,循环结构通常只有三种:简单循环、串接循环和嵌套循环。其它不规则的循环结构都可以转化这三种结构。
1、简单循环的测试假设循环体执行的最大次数为n,在测试时,我们需要考虑的几情况:
(1)零次循环:不执行循环体,直接退出;
(2)一次循环:只执行一次循环体;
(3)二次循环:执行二次循环体;
(4)m次循环:执行循环体m次(m<n-1);
(5)n-1次循环:执行循环体n-1次;
(6)n次循环:执行循环体n次;
(7)n+1次循环:执行循环体n+1次。
例3-11 一段简单的程序代码。
Void testexample3(int n)
{
int i;
int sum=0;
for (i=1;i<=n;i++)
sum=sum+i;
return sum;
}
调用此函数时,设n=10。则对这段循环程序进行测试用例的设计见表3-15
表3-15 testexample3简单循环测试用例
ID | 循环取值 | 初始值 | 返回值 | 执行循环次数 |
---|---|---|---|---|
n | i | sum | ||
TE3-001 | 0 | 1 | 0 | 0 |
TE3-002 | 1 | 1 | 1 | 1 |
TE3-003 | 2 | 1 | 3 | 2 |
TE3-004 | 5 | 1 | 15 | 5 |
TE3-005 | 9 | 1 | 45 | 9 |
TE3-006 | 10 | 1 | 55 | 10 |
TE3-007 | 11 | 1 | 66 | 11 |
2、嵌套循环的测试(1)
B.Beizer提出了一种能减少测试数的方法:从最内层循环开始测试,把所有其他循环都设置为最小值;对最内层循环使用简单循环测试方法,而使外层循环的迭代参数(例如,循环计数器)取最小值,并为越界值或非法值增加一些额外的测试;由内向外,对下一个循环进行测试,但保持所有其他外层循环为最小值,其他嵌套循环为“典型”值;继续进行下去,直到测试完所有循环。
(2)对于嵌套循环的测试重点注意事项:
当外循环变量为最小值时,内层循环为最小值和最大值时的运算结果。 当外循环变量为最大值时,内层循环为最小值和最大值时的运算结果。 循环变量的增量是否正确。 何时退出循环。
3、串接循环的测试
串接循环又称为并列循环。如图3-14所示。在对串接循环进行测试时,如果串接循环的各个循环都彼此独立,则可以使用前述的简单循环测试方法来测试串接循环。如果两个循环串接,而且第一个循环的循环计数器值是第二个循环的初始值,则这两个循环并不是独立的。当循环不独立时,建议使用嵌套循环测试方法来测试串接循环。
3.4.4数据流测试
1、数据流的基本概念
(1)变量的定义和使用
变量一般通过赋值来定义和初始化,并在表达式中被使用。
(2)c-use 和p-use
如果一个变量被用在赋值语句的表达式、输出语句中,或者被当作参数传递给调用函数,或者被用在下标表达式中,都称为该变量的c-use。其中c表示“计算”。
如果一个变量被用在分支语句的条件表达式中,则称为变量的p-use。其中p表示“谓词”。
(3)全局和局部的定义与使用
一个变量可能在同一个基本块中被定义、使用和重定义。注:我们只关心全局变量的定义和使用,局部定义与使用在研究基于数据流的测试时没有意义。
2、数据流图
程序的数据流图(DFG)又称def-use图,它勾画了程序中变量在不同的基本块间的定义流。与程序的控制流图(CFG)有点相似,并可以从它的CFG中导出。
例3-12 :考虑如下一个基本块,包含两条赋值语句和一个函数调用语句。
P=y+z;
Foo(p+q,number);
A[i]=x+1;
If (x>y) {…}
从此基本块中可知,def={p,A},c-use={y,z,p,q,number,x,i},p-use={x,y}。
构造数据流图基本过程:
(1)计算程序中每个基本块的def 、c-use和p-use。
(2)将结点集中的每个结点与它对应的def 、c-use和p-use关联起来。
(3)针对每个具有非空p-use集并且在条件C处结束的结点,如果条件C为真时执行的边1,C为假时执行的是边2,分别将边1、边2与C,!C关联起来。