软件可靠性牵涉到软件生产的全过程,但最终还是落实到软件产品上。而保证软件可靠性的关键步骤是软件测试。
软件测试是为了发现故障而执行程序的过程。其目的是以尽可能少的时间和人力发现并改正软件中潜在的各种故障及缺陷。因此,软件测试与软件可靠性紧密相关。软件中隐藏的故障数目,直接决定软件的可靠性。所以,在软件投入运行之前必须进行软件测试,以尽可能多地发现软件中的故障,提高软件可靠性。而在软件运行过程中,则需要软件维修测试。
随着人们对软件测试重要性认识的加深以及软件系统的日益复杂,软件测试在整个软件开发周期中所占的比例日益增大。目前,许多软件开发机构已将研制力量的40%以上花费在软件测试上。特殊情况下,对于要求高可靠性的软件,例如飞行控制、核反应堆监控软件等,其软件测试费用甚至高达软件开发其它阶段所用费用总和的3-5倍。
1. 软件测试工程
软件测试的实质是根据软件开发各阶段的规格说明和程序的内部结构精心选取一批测试数据,形成测试用例,并用这些测试用例去驱动被测程序,观察程序的执行结果,验证所得结果与预期结果是否一致,然后做相应的调整。所以,软件测试本身是一个工程。软件测试的团队必须不同于该软件的设计团队,它必须独立地确定软件测试规范、生成测试数据、进行测试结果分析、写出测试报告。这一过程可能要反复进行多次,以达到预定的可靠性。在此过程中,测试数据生成是软件测试的核心与关键。不同测试数据对发现软件故障的能力差别很大。某些测试数据能够有效地发现故障,而其他一些测试数据也可能完全是徒劳。为了节约时间和资源,提高测试效率,必须从大量的输入数据中精心挑选出少数有代表性的测试数据,使得采用这些测试数据能够达到最佳的测试效果,高效地把隐藏的故障揭露出来。为此,探讨如何生成高质高效的测试数据,至关重要。而软件测试生成与被测软件的性质、功能和可靠性要求紧密相关。
2.基于程序结构的测试生成
基于规范的功能测试又称黑盒测试,是以检查程序功能来检测程序是否按照规格说明书的规定正确地运行,它以规范说明为依据,选择和设计测试用例。基于程序的结构测试又称白盒测试,则根据程序的内部结构设计测试用例,检测程序在各种不同的情况下是否都按预定的要求正确地运行。基于规范的测试数据生成和基于程序的测试数据生成是两类常用的软件测试数据生成方法。
结构测试要求对被测程序的结构特性作到一定程度的覆盖。覆盖准则一般有以下几种:
① 语句覆盖
希望用测试数据运行被测程序时,被测程序的每一个语句都能被执行。至少说明每一个语句都是可达的,在该测试条件下,程序是正确的。当然我们并不能保证在其他条件下,程序执行该语句时是否也能正确。
② 分支覆盖
在If … then … else …语句中,条件语句测试执行一次是不够的。因为必须让条件为真和为假,至少执行两次。而在Case语句中必须运行所有的情况,而且,还要运行不属于所列情况的default情况。所以,对这些分支语句,必须覆盖所有可能的分支。分支覆盖率就是考察测试数据覆盖了百分之多少的分支。 [Page]
③路径覆盖
由于程序中有分支、循环、递归、调用、跳变等语句,一个程序可以用一个控制流或数据流图来表示。程序的每一次运行都是沿着某一条路径走的。程序在运行这些测试数据时,沿着许多路径都运行过。如果能沿着所有路径都运行一遍,当然很好。如果能做到完全的路径覆盖,就必然达到100%的分支覆盖和语句覆盖。但是,对于较大的程序,基本上不可能沿着所有路径都运行一遍,因为路径数太多了。运行过的路径数对所有路径数的百分比就是路径覆盖率。
测试数据生成的过程就是在输入域中,寻找满足测试准则(如语句覆盖、分支覆盖或路径覆盖)的输入数据的过程。
关于结构测试,有三种测试数据生成方法:随机测试数据生成方法、面向目标的测试数据生成方法以及面向路径的测试数据生成方法。
对于给定的语句(分支或路径),随机测试数据生成方法是在输入域内随机选取测试数据,使得给定语句被执行。
面向目标的测试数据生成方法,依据程序控制流信息,将程序中所有的分支分成两类:一类是影响目标结点的分支,另一类是不影响目标结点的分支。测试数据生成时,根据影响目标结点的那些分支,生成相应的测试数据,使得给定语句被执行。为了能按照预定的分支,走向给定语句,数据依赖分析是必要的。分析数据依赖关系,识别影响给定语句执行的那些语句,即先于给定结点执行的结点序列,再考虑影响目标结点的那些分支,可以提高测试数据生成效率。面向目标的测试数据生成方法,与路径选择无关,虽然可以达到语句覆盖、分支覆盖,但在结构测试覆盖准则中,路径覆盖率是较低的,只有当程序中每一条路径都被测试了,才能说程序得到了全面的检验。
面向路径的测试数据生成方法,首先确定一条经过给定语句的程序路径,这条路径可以自动生成,也可以人为指定。然后在输入域中寻找输入数据,使得在此输入之下,程序沿该路径运行,从而使得给定语句被执行。面向路径的测试数据生成又分为符号执行和实际程序执行两类。符号执行允许程序输入常量、符号值、符号表达式等,以符号计算代替实际执行的数值计算,产生一个符号输入值的代数表达式,即路径约束,它是选定路径的谓词系统。实际上是对输入数据的限制要求,由多个不等式(等式)组成。通过求解不等式,求取满足路径上各限制谓词的测试数据。若系统无解,则相应的路径为不可行路径。与逻辑电路测试类似,软件测试也可以基于故障来产生。用代数约束来描述检测特定类型故障的测试数据。符号执行能够判定路径的可行性,一次符号测试的结果代表了一类普通测试的运行结果,因此测试成本较低。但在遇到循环、过程调用、动态数据结构、数组和指针处理时,符号执行实现困难。基于程序实际执行的测试数据生成方法可以对选定路径上的分支谓词,写出一个逻辑表达式,再进行布尔函数极小化,以确定输入数据,使程序执行沿选定路径进行。也可以利用迭代逼近法,求取满足选定路径上所有谓词的输入值。每次迭代中,执行与选定路径谓词有关的语句,得到一个线性约束集,求解该线性约束集,获得一个输入增量,进而得到下次迭代的输入值,最终产生选定路径的测试数据。也可以用约束求解的方法生成测试数据,将被测程序转化成一个约束系统,然后寻找经过给定语句的路径,生成相应的测试数据。基于程序实际执行的测试数据生成方法,在程序执行的每一步,数组下标、指针值都是确定的。因此,对数组和指针的处理方便,但其测试数据生成与路径选择有关,而判定所选路径是否为可行路径是一件非常困难的事情。 [Page] 随机测试数据生成、面向目标的测试数据生成和面向路径的测试数据生成方法大多只利用控制流信息生成测试数据。而利用数据流信息生成测试则是另一类测试生成方法。该方法将数据流信息应用到路径选择中,并定义了相应的测试覆盖准则。例如,所有定义引用路径覆盖准则是指在一个变量被定义之后,所有引用该变量的语句都必须被覆盖。而所有定义覆盖准则则要求所有定义变量的语句都必须被覆盖。所有计算引用覆盖准则要求所有计算出来的变量被引用的通路都必须被覆盖。数据流测试已经开发了一些软件测试工具,例如ATAC,能够评估测试集的覆盖率,识别未被覆盖的程序范围等,有利于指导选择测试用例,提高测试覆盖率。但是,无论哪一种结构测试,即使其覆盖率达到百分之百,也不能保证把所有隐藏的程序缺陷都揭露出来。