为什么要进行白盒测试?
如果所有软件错误的根源都可以追溯到某个唯一原因,那么问题就简单了。然而,事实上一个bug 常常是由多个因素共同导致的,如下图所示。
黑盒查不到的问题
假设此时开发工作已结束,程序送交到测试组,没有人知道代码中有一个潜在的被 0 除的错误。若测试组采用的测试用例的执行路径没有同时经过x=0和y=5/x进行测试,显然测试工作似乎非常完善,测试用例覆盖了所有执行语句,也没有被 0 除的错误发生。
白盒测试定义
白盒测试将被测程序看作一个打开的盒子,测试者能够看到被测源程序,可以分析被测程序的内部结构,此时测试的焦点集中在根据其内部结构设计测试用例。
又称为 结构测试 或 逻辑驱动测试
白盒测试遵循的原则
采用白盒测试方法必须遵循以下几条原则,才能达到测试的目的:
- 保证一个模块中的所有独立路径至少被测试一次。
- 所有逻辑值均需测试真 (true) 和假 (false) 两种情况。
- 检查程序的内部数据结构,保证其结构的有效性。
- 在上下边界及可操作范围内运行所有循环。
程序的结构形式是白盒测试的主要依据。
程序结构主要用 流程图 N-S图来表示(p.65)
程序的执行路径数目庞大,让程序的所有路径都执行一次,全面的白盒测试将产生百分之百正确的程序,实际上是不可能的
对一个具有多重选择和循环嵌套的程序,不同的路径数目可能是天文数字。给出一个小程序的流程图,它包括了一个执行20次的循环。
包含的不同执行路径数达520条,对每一条路径进行测试需要1毫秒,假定一年工作365 × 24小时,要想把所有路径测试完,需3170年。
白盒测试方法
白盒测试主要是检查程序的内部结构、逻辑、循环和路径。常用测试用例设计方法有:
- 逻辑覆盖法(逻辑驱动测试)
- 基本路径法
- 循环测试法
- 程序插桩法
逻辑覆盖法
主要是测试覆盖率,以程序内在逻辑结构为基础的测试。包括以下6种类型:
- 语句覆盖
- 判定覆盖
- 条件覆盖
- 判定-条件覆盖
- 条件组合覆盖
- 路径覆盖。
语句覆盖
语句覆盖就是设计若干个测试用例,运行被测程序,使得每一可执行语句至少执行一次。
在例图中,正好所有的可执行语句都在路径L1上,所以选择路径 L1设计测试用例,就可以覆盖所有的可执行语句。
语句覆盖率
已执行的可执行语句占程序中可执行语句总数的百分比
复杂的程序不可能达到语句的完全覆盖
语句覆盖率越高越好
语句覆盖的优点
检查所有语句
结构简单的代码的测试效果较好
容易实现自动测试
代码覆盖率高
如果是程序块覆盖,则不涉及程序块中的源代码
语句覆盖不能检查出的错误
逻辑运算(&&、||)错误
判定的第一个运算符“&&”错写成“||”,或第二个运算符“||”错写成“&&”,这时使用上述的测试用例仍然可以达到100%的语句覆盖.
循环语句错误
循环次数错误
跳出循环条件错误
语句覆盖总结
【优点】 :可以很直观地从源代码得到测试用例,无须细分每条判定表达式。
【缺点】 :由于这种测试方法仅仅针对程序逻辑中显式存在的语句,但对于隐藏的条件是无法测试的。如在多分支的逻辑运算中无法全面的考虑。语句覆盖是最弱的逻辑覆盖。
判定覆盖 Decision Coverage
判定覆盖又称为分支覆盖。判定覆盖就是设计若干个测试用例,运行被测程序,使得程序中每个判断的取真分支和取假分支至少经历一次。
【优点】:判定覆盖具有比语句覆盖更强的测试能力。同样判定覆盖也具有和语句覆盖一样的简单性,无须细分每个判定就可以得到测试用例。
【缺点】:往往大部分的判定语句是由多个逻辑条件组合而成,若仅仅判断其整个最终结果,而忽略每个条件的取值情况,必然会遗漏部分测试路径。判定覆盖仍是弱的逻辑覆盖。
条件覆盖Condition Coverage
在设计程序中,一个判定语句是由多个条件组合而成的复合判定,判定(a)&&(b||c)包含了三个条件:a,b和c。为了更彻底的实现逻辑覆盖,可以采用条件覆盖。
条件覆盖就是设计若干个测试用例,运行被测程序,使得程序中每个判断的每个条件的可能取值至少执行一次。
【优点】:增加了对条件判定情况的测试,增加了测试路径。
【缺点】:条件覆盖不一定包含判定覆盖。条件覆盖只能保证每个条件至少有一次为真,而不考虑所有的判定结果。
判定-条件覆盖CDC
判定/条件覆盖实际上是将判定覆盖和条件覆盖结合起来的一种方法,
就是设计足够的测试用例,使得判断中每个条件的所有可能取值至少执行一次,同时每个判定的可能结果也至少出现一次。
设计测试用例覆盖4个条件的8种取值以及4个判定分支。
【优点】 :能同时满足判定、条件两种覆盖标准。
【缺点】 :判定/条件覆盖准则的缺点是未考虑条件的组合情况。
条件组合覆盖
条件组合覆盖就是设计足够的测试用例,运行被测程序,使得每个判断的所有可能的条件取值组合至少执行一次。
【优点】 :条件组合覆盖准则满足判定覆盖、条件覆盖和判定/条件覆盖准则。
【缺点】 :线性地增加了测试用例的数量。
总结
从前面的例子我们可以看到,采用任何一种覆盖方法都不能满足我们的要求,所以,在实际的测试用例设计过程中,可以根据需要将不同的覆盖方法组合起来使用,以实现最佳的测试用例设计 。
基本路径测试
路径测试就是从一个程序的入口开始,执行所经历的各个语句的完整过程。从广义的角度讲,任何有关路径分析的测试都可以被称为路径测试。
完成路径测试的理想情况是做到路径覆盖,但对于复杂性大的程序要做到所有路径覆盖是不可能的。
在不能做到所有路径覆盖的前提下,如果某一程序的每一个独立路径都被测试过,那么可以认为程序中的每个语句都已经检验过了,即达到了语句覆盖。这种测试方法就是通常所说的基本路径测试法。
基本路径测试方法是在控制流图的基础上,通过分析控制结构的环形复杂度,导出执行路径的基本集,再从该基本集设计测试用例。基本路径测试方法包括4个步骤:
(1)画出程序的控制流图。
(2)计算程序的环形复杂度,导出程序基本路径集中的独立路径条数,这是确定程序中每个可执行语句至少执行一次所必须的测试用例数目的上界。
(3)导出基本路径集,确定程序的独立路径。
(4)根据(3)中的独立路径,设计测试用例的输入数据和预期输出。
控制流图
程序流程图又称框图,是我们最熟悉,也是最容易理解的一种程序控制结构的图形表示了。在这种图上的框里面常常标明了处理要求或者条件,但是,这些标注在做路径分析时是不重要的。为了更加突出控制流的结构,需要对程序流程图做一些简化。
节点
1、标有编号的圆圈
2、程序流程图中矩形框所表示的处理
3、菱形表示的两个甚至多个出口判断
4、多条流线相交的汇合点
控制流线或弧
1、箭头
2、与程序流程图中的流线一致,表 明了控制的顺序
3、控制流线通常标有名字
在选择或多分支结构中,分支的汇聚处应有一个汇聚节点。
边和结点圈定的区域叫做区域,当对区域计数时,图形外的区域也应记为一个区域。
复合条件的控制流图
如果判断中的条件表达式是由一个或多个逻辑运算符 (OR, AND, …) 连接的复合条件表达式,则需改为 一系列只有单个条件的嵌套的判断。
程序环路复杂性
环路复杂性即McCabe复杂性度量,在进行程序的基本路径测试时,从程序的环路复杂性可导出程序基本路径集合中的独立路径条数。
程序的环路复杂性给出了程序基本路径集中的独立路径条数,这是确保程序中每个可执行语句至少执行一次所必需的测试用例数目的上界。
独立路径:指包括一组以前没有处理的语句或条件的一条路径。
从控制流图来看,一条独立路径是至少包含有一条在其它独立路径中从未有过的边的路径。
一条路径这里指一条“程序通路”。
导出测试用例
导出测试用例,确保基本路径集中的每一条路径的执行。
根据判断结点给出的条件,选择适当的数据以保证某一条路径可以被测试到 — 用逻辑覆盖方法。
每个测试用例执行之后,与预期结果进行比较。如果所有测试用例都执行完毕,则可以确信程序中所有的可执行语句至少被执行了一次。
必须注意,一些独立的路径,往往不是完全孤立的,有时它是程序正常的控制流的一部分,这时,这些路径的测试可以是另一条路径测试的一部分。
基本路径测试
基本路径测试法的步骤:
(1)以详细或源代码作为基础,导出程序的控制流图。
(2)计算得到控制流图G的环路复杂度V(G)
(3)确定基本路径集,生成测试用例,确保基本路径集中每条路径的执行。
循环测试法
循环分为4种不同类型:
简单循环
嵌套循环
连锁循环(串接循环)
非结构循环(不规则循环)
(1) 简单循环测试
① 零次循环:从循环入口到出口
② 一次循环:检查循环初始值
③ 二次循环:两次通过循环
④ m次循环: 检查多次循环
⑤ 最大次数循环n、比最大次数多一次n+1、少一次的循环n-1。
(2) 嵌套循环测试
① 对最内层循环做简单循环的全部测试。所有其它层的循环变量置为最小值;
② 逐步外推,对其外面一层循环进行测试。测试时保持所有外层循环的循环变量取最小值,所有其它嵌套内层循环的循环变量取“典型”值。
③ 反复进行,直到所有各层循环测试完毕。
④ 对全部各层循环同时取最小循环次数,或者同时取最大循环次数
(3) 连锁循环
如果各个循环互相独立,则可以用与简单循环相同的方法进行测试。但如果几个循环不是互相独立的,则需要使用测试嵌套循环的办法来处理。
(4) 非结构循环
这一类循环应该使用结构化程序设计方法重新设计测试用例。
1.对于最多为n次的简单循环,要测试n-1次,n次,n+1次。最多为n次,n+1次怎么测;
2.第一问的条件下,若第一步i赋值为1,然后判断i是否小于m,m是要输入的值,如果m随便输入,无上界,那么n次,n+1次怎么判断,我m不同,最大次数不也不同?
3.嵌套循环内层测试时,外层循环取最小值,最小值什么意思,是外层不循环,还是循环一次?
Z路径覆盖
路径覆盖是白盒测试最为典型的问题。
完成路径测试的理想情况是做到路径覆盖,对于比较简单的小程序实现路径覆盖是可以做到的,但是如果程序中出现多个判断和多个循环,可能的路径数目将会急剧增长,达到天文数字,以至于实现完全的路径覆盖是不可能的。
为了解决这一问题,我们必须舍掉一些次要因素,对循环机制进行简化,从而极大地减少路径的数量,使得覆盖这些有限的路径成为可能。这种简化意义下的路径覆盖称为Z路径覆盖
注意:
循环化简是指限制循环的次数。无论循环的形式和实际执行循环体的次数是多少,只考虑循环一次和零次两种情况。即:只考虑执行时进入循环体一次和跳过循环体的两种情况。
程序插桩技术
方法简介
如果我们想要了解一个程序在某次运行中所有可执行语句被覆盖的情况,或是每个语句实际执行次数,最好的办法就是利用程序插桩技术.
在软件动态测试中,程序插桩(Program Instrumentation)是一种基本的测试手段
借助往被测程序中插入操作,来实现测试目的的方法。
最简单的插桩:在程序中插入打印语句printf(“…”)语句
程序从入口开始执行,到出口结束,凡经历的计数语句都能记录下该程序点的执行次数。
如果我们在程序的入口处还插入了对计数器C(i)初始化的语句,在出口处插入了打印这些计数器的语句,就构成了完整的插桩程序。它就能记录并输出在各程序点上语句的实际执行次数.
设计插桩程序时需要考虑的问题包括:
(1)需要探测哪些信息
(2)在程序的什么部位设置探测点
(3)需要设置多少个探测点
前两个问题需要结合具体的问题解决,并不能给出笼统的回答。至于第三个问题,需要考虑如何设置最少的探测点!
静态测试法
静态测试不实际运行软件,只是检查和审阅,主要对软件的编程格式,结构等方面进行评估。
静态测试
代码审查(code inspection)
程序员和测试员组成的审查小组通过阅读、讨论和争议,对程序进行静态分析的过程 。
第一步:小组成员提前阅读设计规格书、程序文本等相关文档
第二步:召开程序审查会,开发人员读程序,审查小组讨论、发现、解决问题
内容
检查代码和设计的一致性
检查代码对标准的遵循、可读性
检查代码的逻辑表达的正确性
检查代码结构的合理性
形成公司积累变成容易出错的checklist
代码走查(code walkthrough)
程序员和测试员组成的审查小组通过逻辑运行程序,发现问题。
第一步:小组成员提前阅读设计规格书、程序文本等相关文档
第二步:利用测试用例,使程序逻辑运行,记录程序的踪迹,发现、讨论、解决问题
桌面检查
可视为由单人进行的代码检查或代码走查。
是程序员对源程序代码进行分析、检验,并补充相关的文档,发现程序中的错误的一种方法。
白盒测试综合策略
在白盒测试中,可以使用各种测试方法的综合测试如下所示:
在测试中,应尽量先用工具进行静态结构分析。
测试中可采取先静态后动态的组合方式:先进行静态结构分析、代码检查和静态质量度量,再进行覆盖率测试。
利用静态分析的结果作为引导,通过代码检查和动态测试的方式对静态分析结果进行进一步的确认,使测试工作更为有效。
覆盖率测试是白盒测试的重点,一般可使用基本路径测试法达到语句覆盖标准;对于软件的重点模块,应使用多种覆盖率标准衡量代码的覆盖率;
在不同的测试阶段,测试的侧重点不同:在单元测试阶段,以代码检查、逻辑覆盖为主;在集成测试阶段,需要增加静态结构分析、静态质量度量;在系统测试阶段,应根据黑盒测试的结果,采取相应的白盒测试。