白盒测试(White Box Testing),又称结构测试、逻辑驱动测试或基于程序的测试,是
软件测试的重要方法。它根据软件产品的内部工作过程,在
计算机上进行测试,以证实每种内部操作是否符合设计规格要求,所有内部成分是否已经过检查。其目的是发现程序编码过程中的错误。
白盒测试从测试重点来看,可以分为控制流分析技术和数据流分析技术。从是否执行程序的角度来看,可分为静态测试方法和动态测试方法。静态测试方法主要为程序结构分析法,可分为代码走查、代码审查、控制流分析、数据流分析、信息流分析等;动态测试方法主要分为逻辑覆盖法和路径测试法等,运用最广泛的是路径测试法。
白盒测试的静态测试工具主要有Logiscope、PRQA软件等,动态测试工具主要有DevPartner软件、Pure系列等,可以帮助软件测试人员增大代码的覆盖率,提高代码的质量,发现代码中隐藏的问题。
基本概念
白盒测试是在了解产品内部工作的基础上,通过测试来检验产品内部动作是否按照规格说明书的规定进行的。它不管系统的功能,而是按照程序内部的结构测试程序,检验每条程序是否都能按照预定要求正常工作。它一般用来分析程序的内部结构,程序的结构和处理过程像白盒子一样透明,允许测试人员利用程序内部的逻辑结构及有关信息,设计或选择测试用例,对程序所有逻辑路径进行测试。白盒测试主要应用于单元测试和集成测试阶段,通常不适用于系统测试,但在系统描述中涉及动态描述的部分,仍然可以借鉴一些白盒测试的方法进行测试,因此,白盒测试并不仅限于单元测试和集成测试。
目的与原则
白盒测试的主要目的是对程序模块进行检查,检查过程中需要遵循如下原则:
• 保证程序模块的所有独立执行路径至少测试一遍;
• 对所有的逻辑判定,取“真”与取“假”的两种情况都能至少测试一遍;
• 在上下边界及可操作范围内运行所有循环;
动态测试方法
动态测试是在程序运行状态下的测试,即通过输入预先设计好的数据,根据程序运行计算输出的结果,将其与实际输出结果进行比较,进而发现程序中存在的错误。在程序的运行过程中,通过动态测试,测试人员可以判断程序的基本模块、执行过程、子系统以及整体系统的运行结果是否存在缺陷。动态测试方法一共分为三步,首先设定待测试的配置,然后对相关的源程序进行插桩,编译连结生成可执行文件,最后运行插桩后的程序系统自动记录执行路径,得出覆盖率。动态测试方法分为逻辑覆盖法和路径测试法,其中路径测试法运用最为广泛。
逻辑覆盖法
逻辑覆盖是对一系列测试的总称,这种方法要求测试人员对程序的逻辑结构有清楚的理解。根据覆盖目标不同和覆盖源程序语句的详尽程度,常用的逻辑覆盖可分为语句覆盖、判定覆盖、条件覆盖、判定—条件覆盖、条件组合覆盖、路径覆盖等六种。
语句覆盖
语句覆盖就是设计若干个测试用例,然后运行被测程序,使得每一条可执行语句至少执行一次。这种覆盖使得程序中每个可执行语句都得到执行,但它是最弱的逻辑覆盖标准,效果有限,必须与其他方法交互使用。
在上图案例中,若要使图中每个语句都执行一次,程序的执行路径应该是ace,通过a=2,b=3,x=0(x可以是任何数)这一测试数据可以覆盖该路径。在这一案例中,两个判断条件都只测试了条件为真的情况,如果条件为假时处理有误,则不能发现。因此该测试是不充分的,语句覆盖能力较弱,无法发现程序中某些逻辑运算符和逻辑条件的错误。
判定覆盖
判定覆盖就是设计若干个测试用例,然后运行被测程序,使得程序中每个判断的取真分支和取假分支至少经历一次。
对于案例中的被测试模块流程图,通过路径ace和abd,或路径acd和abe,即可达到判定覆盖标准。例如,a=3,b=0,x=3便可以覆盖acd路径,a=1,b=1,x=1可以覆盖abe路径。
判定覆盖略胜于语句覆盖,但还不能保证能查出在判断的条件中存在的错误。因此,还需要更强的逻辑覆盖准则去检验判断内部条件。
条件覆盖
条件覆盖就是设计若干个测试用例,然后运行被测程序,使得程序中每个判断的每个条件的可能取值至少执行一次。
对于案例中的被测试模块流程图,共有两个判定表达式,每个表达式中有两个条件,为了做到条件覆盖,应该选取测试数据使得以下四个条件的可能取值至少执行一次:
设计下述两组测试数据满足条件覆盖:
①a=2,b=0,x=4(满足a>1,b=0;a=2,x>1,执行路径ace)
②a=1,b=1,x=1(满足a≤1,b≠0;a≠2,x≤1,执行路径abe)
条件覆盖深入到判定中的每个条件,但可能不能满足判定覆盖的要求。例如如果使用下面两组测试数据,则只满足条件覆盖标准并不满足判定覆盖标准。
①a=2,b=0,x=1(满足a>1,b=0;a=2,x≤1,执行路径ace)
②a=1,b=1,x=2(满足a≤1,b≠0;a≠2,x>1,执行路径abe)
判定—条件覆盖
判定-条件覆盖就是设计足够的测试用例,使得判断中每个条件的所有可能取值至少执行一次,同时每个判断本身的所有可能判断结果至少执行一次,即要求各个判断的所有可能的条件取值组合至少执行一次。
对于案例中的被测试模块流程图,下面两组测试数据能达到判定-条件覆盖标准:
①a=2,b=0,x=4(满足x>1,b=0;a=2,x>1)
②a=1,b=1,x=1(满足a≤1,b≠0;a≠2,x≤1)
判定-条件覆盖也是有缺陷的。从表面上来看,它测试了所有条件的取值,但某些条件掩盖了另一些条件,会遗漏某些条件取值错误的情况。
条件组合覆盖
多重条件覆盖就是设计足够的测试用例,运行被测程序,使得每个判断的所有可能的条件取值组合至少执行一次。
对于案例中被测模块流程图中的条件组合:
下面的4组测试数据可以使上面列出的8种组合至少出现一次:
①a=2,b=0,x=3(针对①和⑤两种组合,执行路径ace);
②a=2,b=1,x=1(针对②和⑥两种组合,执行路径abe);
③a=1,b=0,x=3(针对③和⑦两种组合,执行路径abe);
④a=1,b=1,x=1(针对④和⑧两种组合,执行路径abd)。
条件组合是一种比较强的覆盖准则,可以有效地检查各种可能的条件取值的组合是否正确。它不但可覆盖所有条件的可能取值的组合,还可覆盖所有判断的可取分支,但可能有的路径还是会遗漏掉,测试还不完全。
路径覆盖
路径覆盖,就是设计足够多的测试用例,执行程序所有可能的路径,这是覆盖率最高的一种覆盖技术。
根据上方案例图,通过路径abd,abe,acd,ace,即可达到路径覆盖标准。例如,x=4,y=-3,z=2便可以覆盖ace路径,x=-1,y=1,z=-1可以覆盖abd路径。但是,由于路径覆盖需要对所有可能的路径进行测试(包括循环、条件组合、分支选择等),因此需要设计大量且复杂的测试用例,从而使工作量呈指数级增长。
Z路径覆盖
路径覆盖由于路径数量过多,使用起来多有不便,人们便舍掉了路径覆盖的一些次要因素,对循环机制进行简化,极大地减少了路径的数量,使得覆盖这些有限路径成为可能,这种覆盖方法称为Z路径覆盖。循环化简指的是限制循环的次数,只考虑循环执行一次和零次两种情况,经过Z路径覆盖法对程序中循环的简化后,程序中只存在两种结构:顺序结构和分支结构。
逻辑覆盖标准对比
路径测试法
白盒测试有两种常用技术,一种是覆盖测试,另一种是路径测试。从流程图上讲,程序的一次执行对应着从入口到出口的一条路径,针对路径的测试即为路径测试。从广义的角度讲,任何有关路径分析的测试都可以被称为路径测试。路径测试是在程序控制流图的基础上,通过分析控制构造的环路复杂性,导出基本可执行路径集合,从而设计测试用例。
程序路径表达
在对路径进行分析的时候,首先要解决的是确定每个路径以及路径数目。为了更加直观和形象地表达出每条路径对于某条路径,可采用弧序列或者节点序列的方式并引了两个运算:加和乘。弧a和弧b相加,表示为a+b,它表明两条弧是“或”的关系,是并行的路径;弧a和弧b相乘,表示为ab,它表明路径是先经历弧a,接着再经历弧b,弧a和弧b是先后相接的。
路径表达式运算满足以下规律:
程序的环路复杂性
环路复杂性V(G)的计算方式有以下三种:
基路径测试
如果把覆盖的路径数压缩到一定限度内,就成为基路径测试。基路径测试是在程序控制流图的基础上,通过分析控制构造的环路复杂性,导出基本可执行路径集合,从而设计测试用例的方法。设计出的测试用例要保证在测试中,程序的每一条可执行路径至少要执行一次。基路径测试法包括以下五个方面:
1.根据详细设计或者程序源代码,绘制出程序的程序流程图;
2.根据程序流程图,绘制出程序的控制流图;
3.计算程序的环路复杂性。环路复杂性是一种为程序逻辑复杂性提供定量测试的软件度量,将该度量用于计算程序的基本独立路径数目;
4.找出独立路径。通过程序的程序流程图导出基本路径集,列出程序的独立路径;
5.设计测试用例。根据程序结构和程序环路复杂性设计用例输入数据和预期结果,确保基本路径集中的每一条路径的执行。
每个测试用例执行之后,与预期结果进行比较,如果所有的测试用例都执行完毕,则可以确信程序中所有的可执行语句至少被执行了一次。但一些独立的路径往往不是完全孤立的,有时它是程序正常的控制流的一部分,那这些路径的测试可以是另一条路径测试的一部分。
循环测试
循环测试专用于测试程序中的循环,并且可以进一步提高测试覆盖率。从本质上来说,循环测试的目的就是检查循环结构的有效性。通常,循环可以划分为简单循环、嵌套循环、连锁循环和非结构循环。
静态测试方法
静态测试方法主要为程序结构分析法,可分为代码审查、代码走查、控制流分析、数据流分析、信息流分析等。
代码审查
代码审查是由若干程序员和测试人员组成审查小组,通过阅读、讨论和争议,对程序进行静态分析的过程。代码审查分两步:第一步,小组负责人提前把设计规格说明书、控制流程图、程序文本及有关要求、规范等分发给小组成员,作为审查的依据。小组成员在充分阅读这些材料后进行审查的第二步,召开程序审查会。在会上,首先由程序员讲解程序的逻辑,程序员及其他小组成员可以提出问题,展开讨论,审查错误是否存在。例如,在某个局部性小问题修改方法的讨论,可能发现与之牵连的其他问题,甚至设计模块的功能说明、模块间接口和系统总体结构的大问题,从而导致对需求的重定义、重设计、重验证,进而大大改善了软件质量。
代码走查
代码走查与代码审查基本相同,其过程分为两步。第一步也是把材料先发给走查小组每个成员,认真研学后再开会。开会审查的过程与代码审查不同,不是简单地读程序和对照错误检查表进行检查,而是让与会者充当
计算机,即首先由测试组成员为所测程序准备一批有代表性的测试用例,提交给走查小组。走查小组开会,集体充当计算机的角色,让测试用例按程序逻辑运行一遍,随时记录程序的踪迹,供分析和讨论用。
控制流分析
控制流分析指的是检查程序的控制结构,以验证程序结构的一些规则在程序编写过程中是否得到遵循,在此基础上得到有关结构成分的语法树并揭示控制结构的缺陷。对于控制流关系的图被称为控制流图,图上的每个节点对应一个程序元素,两个节点间一个直接的弧,表示相应的两个元素再控制流关系中组成一个顺序对。控制流图中的一条路径对应于一个潜在可执行的程序元素顺序。执行一条路径就是执行对应的程序元素序列。如果一个输入引起一条路径的执行,那么这条路径就是可达的,否则就是不可达的。通常,含循环的路径有无线多条,即使没有循环,一个程序也可能有非常多的路径需要分析。它有线性结构、基本的条件判断、多分支的条件判断、while-do循环、do-while循环五种基本程序结构。控制流分析主要从三个方面来分析程序结构:条件判定节点本身的复杂性;条件判定节点与循环节点对执行路径产生的影响;循环本身的复杂性。
数据流分析
数据流分析最初是随着
编译系统生成有效的目标代码而出现的,这类方法主要用于优化代码。数据流测试是基于程序的控制流,从建立的数据目标状态的序列中发现异常的结构测试方法。其基本思想是一个变量的定义,通过辗转的引用和定义,可以影响到另一个变量的值,或者影响到路径的选择等。进行数据流测试时,根据被测试程序中的变量的定义和引用位置选择测试路径。主要遵循以下覆盖准则:
信息流分析
信息流分析主要用于验证程序变量间信息的传输是否遵循保密要求,通过对输入数据、输出数据、语句之间的关系的分析来检查程序错误。信息流分析主要用于分析是否存在无用的语句。信息流分析能够列出对输入变量的所有可能的引用,在程序的任何指定点检查其执行是否影响某一输出变量值的语句,为输入输出关系提供一种检查,看每个输出值是否由相应的输入值导出。
其他测试方法
程序插装
程序插装使被测程序在保持原有逻辑完整性基础上,在程序中插入一些探针,通过探针的执行,抛出程序的运行特征数据。基于这些特征数据分析,可以获得程序的控制流及数据流信息,进而得到逻辑覆盖等动态信息。程序中什么部位设置探针、如何设计探针以及探针函数捕获数据的编码和解码。
程序插装在实践中应用广泛,可以用来捕获程序执行过程中变量值的变化情况,也可以用来检测程序的分支覆盖和语句覆盖。程序插装的关键技术包括要探测哪些信息、在程序中什么部位设置探针、如何设计探针以及探针函数捕获数据的编码和解码。
域测试
域测试的“域”是指程序的输入空间,域测试方法基于对输入空间的分析,其理想结果就是检验输入空间中的每一个输入元素是否都产生正确结果。而输入空间又可以分为不同的子空间,每一子空间对应一种不同的计算。在考察被测试程序的结构后发现,子空间的划分是由程序中分支语句的谓词决定,输入空间的一个元素,经过程序中某些特定语句的执行而结束。域测试正是在分析输入域的基础上,选择恰当的测试点后进行测试的。
符号测试
符号测试沿用传统的程序测试方法,通过运行被测试程序来验证它的可靠性。此外,由于一次符号测试的结果代表了一大类普通测试的运行结果,实际上是证明程序接受此类输入,所得输出结果是否正确。从符号测试的使用方法来看,问题的关键在于开发出比传统的编译器功能更强。能够处理符号运算的编译器和解释器。
程序变异
程序变异测试是一种错误驱动测试,它针对某类特定程序错误。因为人们无法查找出程序中的所有错误,所以比较现实的方法是将错误的范围尽可能地缩小,以利于专门测试某类错误是否存在。这样便于把目标集中于对软件危害最大的可能错误,取得较高的测试效率,并降低测试成本。
测试覆盖准则
测试覆盖准则有K.A.Foster的ESTCA覆盖准则和Woodward等人的层次LCSAJ覆盖准则。Foster的经验型覆盖准则是从硬件的早期测试方法中得到启发的,通过大量的实验确定了程序中谓词最容易出错的部分,得出了一套错误敏感测试用例分析ESTCA(Error Sensitive Test Cases Analysis)规则。Woodward等人提出了一种层次LCSAJ覆盖准则,LCSAJ (Linear Code Sequence and Jump)的意思是线性代码序列与跳转,一个LCSAJ是一组顺序执行的代码,以控制流跳转为其结束点,它表明越是高层的覆盖准则越难满足。
ESTCA覆盖准则
ESTCA覆盖准则也叫错误敏感测试用例分析规则,是根据程序中的谓词最容易出错的部分得出的规则,属于
大数据的范畴。ESTCA覆盖准则针对程序编写人员容易发生的错误,或是围绕着发生错误的频繁区域,从而提高发现错误的概率,它具有三点规则。
规则一:对于A rel B(rel可以是<、=和>)型的分支谓词,应适当选择A与B的值,使得测试执行到该分支语句时,A<B、A=B和A>B的情况分别出现一次。
规则二:对于A rel1 C(rel1可以是>或<,A是变量,C是常量)型的分支谓词,当rel1为<时,应适当选择A的值,使A=C-M。(M是距C最小的容器允许的正数,若A和C均为整型,则M=1)。同样,当rel1为>时,应适当选择A的值,使A=C+M。
规则三:对外部输入变量赋值,使其在每一测试用例中均有不同的值和符号,并与同一组测试用例中其他变量的值和符号不一致。
LCSAJ覆盖准则
LCSAJ覆盖准则也叫线性代码序列与跳转,一个LCSAJ是一组顺序执行的代码,以控制流跳转为其结束点,LCSAJ的起点是程序第一行或转移语句的入口点或控制流可以到达的点,几个首尾相连,最后一个LCSAJ终点为程序终点的LCSAJ串组成程序的一条路径。LCSAJ覆盖准则是一个分层覆盖准则:
第一层用语句覆盖;
第二层是分支覆盖;
第三层是一段LCSAJ覆盖,即程序的每一个LCSAJ都至少在测试中经历一次;
第四层就是任意相连的两段LCSAJ覆盖,即程序中每两个首尾相连的LCSAJ组合起来在测试中都要经历一次;
第五层就是任意相连的三层LCSAJ覆盖;
第n+2层就是每n个首尾相连的LCSAJ组合在测试中都要经历一次,以此类推。
测试方法选择
白盒测试中测试方法的选择策略如下:
1.在测试时,首先进行静态结构分析。
2.采用先静态后动态的组合方式,在进行静态结构分析、代码检查和静态质量度量后,再进行覆盖测试。
3.利用静态分析的结果,通过代码检查和动态测试的方法对结果做进一步确认,使测试工作更为有效。
4.覆盖率测试是白盒测试的重点,使用基路径测试达到语句覆盖标准,对于重点模块,应使用多种覆盖标准衡量代码的覆盖率。
5.不同的测试阶段,侧重点不同。
测试工具
适用范围
白盒测试适用于单元测试、集成测试和回归测试。单元测试指的是检查和验证软件中的最小可测试单元,单元是一个软件组件,不能再细分为其他组件,软件工程师编写白盒测试用例来检查单元编码是否正确。
集成测试将软件组件、硬件组件或两者结合起来进行测试,以评估它们之间的交互作用。测试人员可以编写白盒测试用例和黑盒测试用例来明确检查各种单元之间的接口。
回归测试,即对系统或组件进行选择性重新测试,以验证修改不会造成意外影响,并且系统或组件仍符合其规定要求。回归测试可以通过黑盒测试用例、白盒测试用例或两者的组合来完成,白盒测试和集成测试用例可以作为回归测试的一部分保存并允许。
与黑盒测试对比