章七 带上X光眼镜测试软件
本章讲四个基本测试之中的第四个——动态白盒测试。另三个为静态黑盒(测试产品说明书)、动态黑盒(测试软件)和静态白盒(检查程序代码)。
一、动态白盒测试
1、动态白盒测试是指利用查看代码功能(做什么)和实现方式(怎么做)得到的信息来确定哪些需要测试、哪些不要测试、如何开展测试。
2、动态白盒测试的另一个常用名称是结构化测试(structural testing),因为软件测试员可以查看并使用代码的内部结构,从而设计和执行测试。
3、动态白盒测试不仅仅是查看代码的运行情况,还包括直接测试和控制软件,动态白盒测试包括以下4个部分:
(1)直接测试底层函数、过程、子程序和库。在windows中称为API。
(2)以完整程序的方式从顶层测试软件,但是根据对软件运行的了解调整测试用例。
(3)从软件获得读取变量和状态信息的访问权,以便确定测试与期望结果是否相符,同时,强制软件以正常测试难以实现的方式运行。
(4)估算执行测试时“命中”的代码量和具体代码,然后调整测试,去掉多余的测试用例,补充遗漏的用例。
二、动态白盒测试和调试
不要混淆动态白盒测试和调试,两者表面上相似,因为它们都包括处理软件缺陷和查看代码的过程,但目标大不相同。
动态白盒测试的目标是寻找软件缺陷,调试的目标是修复缺陷。然而它们在隔离软件缺陷的位置和原因上确实存在交叉现象。
执行这些底层的测试,会用到许多和程序员使用的相同的工具。如果程序编译过,可能会使用相同的编译器。
可能会使用代码级的调试器来单步跟踪程序,观察变量,设置断点,等等。也可能自己编写程序来分别测试需要检查的模块代码。
三、分段测试
一般产生高额费用的原因有:
(1)难以找出导致问题的原因;
(2)某些软件缺陷掩盖了其它软件缺陷,测试可能失败。
1、单元测试和集成测试
解决上述问题的方法当然是一开始就不让它发生。如果代码分段构建和测试,最后合在一起形成更大的部分,那么整个产品无疑会链接在一起。
在底层进行的测试称为单元测试(unit testing)或者模块测试(module testing)。单元经过测试,底层软件缺陷被找出并修复之后,就集成在一起,对模块的组合进行集成测试(integration testing)。这个不断增加的测试过程继续进行,加入越练越多的软件片段,直至整个产品——至少是产品的主要部分——在称为系统测试(system testing)的过程中一起测试。
采取这种测试策略很容易隔离软件缺陷。
在单元级发现问题时,问题肯定就在那个单元中,如果在多个单元集成时发现软件缺陷,那么它一定与模块之间的交互有关。当然也有例外。
这种递增测试有两条途径:自底向上(bottom-up)和自顶向下(top-down)。
在自底向上测试中,要编写称为测试驱动的模块调用正在测试的模块。测试驱动模块以和将来真正模块同样的方式挂接,向处于测试的模块发送测试用例数据,接受返回结果,验证结果是否正确。采取这种方式,可以对整个软件进行非常全面的测试,为它提供全部类型和数量的数据,甚至高层难以发送的数据。
自顶向下测试有点像小规模的大爆炸测试。
2、单元测试用例
在进行白盒测试之前,一定要根据说明书建立黑盒测试用例,用这种方式可以真正测试模块的功能和作用。
如果先从模块的白盒角度建立测试用例,检查代码,就会偏向于以模块工作方式建立测试用例。如程序员误解了说明,于是测试用例就会不对。
虽然测试用例精确完整地测试了模块,但是可能不准确,因为没有测试预期的操作。
根据白盒知识增减测试用例时根据程序内部的信息对等价划分的进一步提炼。
四、数据覆盖
查看代码决定如何调整测试用例。合理的方法是像黑盒测试那样把软件代码分成数据和状态。从同样的角度看软件,可以相当容易地把得到的白盒信息映射到已经写完的黑盒测试用例上。
首先考虑数据。数据包括所有的变量、常量、数组、数据结构、键盘和鼠标输入、文件、屏幕输入/输出、以及调制解调器、网络等其它设备的输入和输出。
1、数据流(Data Flow)
数据流覆盖主要是指在软件中完全跟踪一批数据。在单元测试级,数据仅仅通过了一个模块或者函数。同样的跟踪方式可以用于多个集成模块,甚至整个软件产品。
如果在底层测试函数,就会使用调试器观察变量在程序运行时的数据。通过黑盒测试,只能知道变量开始和结束的值。通过动态白盒测试,还可以在程序运行期间检查变量的中间值。根据观察结果就可以决定更改某些测试用例。
2、次边界
以下是次边界导致软件缺陷最常见的例子:
(1)计算税收的模块在某些财务结算处可能从使用数据表转向使用公式。
(2)在RAM底端运行的操作系统也许开始把数据移到硬盘上的临时存储区。这种次边界甚至无法确定,它随着磁盘上剩余空间的数量而发生变化。
(3)为了获得更高的精度,复杂的数值分析程序根据数字大小可能切换到不同的等式以解决问题。
如果进行白盒测试,就需要仔细检查代码,找到次边界条件,并建立能测试它们的测试用例。
3、公式和等式
公式和等式通常深藏于代码中。
撇开代码中的公式和等式,查看它们使用的变量,在程序正常输入和输出之外,为其建立测试用例和等价划分。
4、错误强制(error forcing)
如果执行在调试器中测试的程序,不仅能够观察到变量的值——还可以强制改变变量的值。
注意:在使用错误强制时,小心不要设置现实世界中不可能出现的情况。如果程序员在函数开头检查n值必须大于0,而且n值仅用于该公式中,那么将n值设为0,使程序失败的测试用例就是非法的。
如果仔细选择了错误强制情况,并和程序员一起反复检查以确认它们是合法的,错误强制就是一个有效的工具。
使用错误强制的上好方法就是迫使软件中的所有错误提示信息显示出来。
大多数软件使用内部错误代码表示错误的提示信息。
五、代码覆盖(Code Coverage)
测试数据只是一半的工作。为了全面地覆盖,还必须测试程序的状态及程序流程。必须设法进入和退出每一个模块,执行每一行代码,进入软件每一条逻辑和决策分支。这种类型的测试叫做代码覆盖测试。
代码覆盖测试是一种动态白盒测试。
代码覆盖测试最简单的形式是利用编译环境的调试器通过单步执行程序查看代码。
对于小程序或者单独模块,使用调试器就足够了。然而对大多数程序进行代码覆盖测试要用到称为代码覆盖率分析器(code coverage analyzer)的专用工具。
代码覆盖率测试器挂接在正在测试的软件中,当执行测试用例时在后台执行。每当执行一个函数,一行代码或一个逻辑决策分支时,分析器就记录相应的信息。从中可以获得指示软件哪些部分被执行,哪些部分未被执行的统计结果。利用该数据可以得到:
(1)测试用例没有覆盖软件的哪些部分;
(2)哪些测试用例时多余的;
(3)为了使覆盖率更好,需要建立什么样的新测试用例;
(4)软件质量的大致情况。
如果测试用例覆盖了软件的90%而未发现任何软件缺陷,就说明软件质量非常好。这并非绝对。注意“杀虫剂现象”,软件测试得越多,它对测试的免疫能力越强,如果测试用例覆盖了软件的90%而未发现任何软件缺陷,也有可能使软件的构造不好——可能是软件对测试具有了免疫能力。增加新的测试用例可能会暴露余下的10%具有非常多的缺陷。
1、程序语句和代码行覆盖
代码覆盖最直接的形式称为语句覆盖(statement coverage)或者代码行覆盖(line coverage)。如果在测试软件的同时监视语句覆盖,目标就是保证程序中每一条语句最少执行一次。
遗憾的是,语句覆盖是一种误导,即使全部语句都被执行了,也不能说走遍了软件的所有路径。
2、分支覆盖
试图覆盖软件中的所有路径称为路径覆盖。路径测试最简单的形式称为分支覆盖测试。
大多数代码覆盖率分析器将根据代码分支,分别报告语句覆盖和分支覆盖的结果,是软件测试员更加清楚测试的效果。
3、条件覆盖
与分支覆盖一样,代码覆盖率分析器可以被设置为在报告结果时将条件考虑在内。如果测试条件覆盖,就能达到分支覆盖,顺带也能达到语句覆盖。