软件测试是一个技术活,但是也要包含从经济和人类心理的角度来思考问题。
在真实世界中我们想在一个发布前的程序中测试所有的可能性,在大多数情况这种是不可能实现的情况。甚至一个简单的程序要和会有上百上千的输入输出组合。给测试案例创造所有的可能性是不切实际的。对一个复杂的应用程序进行完全测试,耗时过长、人力成本过高,从经济效益上来说是不可行的。另外软件测试人员还需要正确的心态才能够正确地对软件应用测试,某些情况下测试人员心态比实际流程本身更重要。
1. 测试心理学:
应用程序测试效果不佳的原因之一,是大多数程序员对测试这个词有着错误的定义。他们会说:
1.测试是证明程序中不存在错误的过程
2.测试的目的是证明程序正确实现了预期功能
3.测试是建立先定系,确认程序按设计正常运行的过程
这些都是本末倒置的,当你测试一个程序中目的是发现程序错误,当你的程序更具价值。通过测试增加价值,意味着提升程序质量或者可靠性,提升程序可靠性意味着发现并修复错误。
因此不要为了证明程序能正常工作而去测试,相反先要假设程序中存在错误然后通过测试发现这些错误。
测试是为了发现错误而执行程序的过程。
软件测试本质上是一个破坏性、近乎"挑剔"的过程,这与人类天生倾向于建设性而非破坏性的本性相悖,这也是为什么大多数人觉得测试很难。更重要的是,我们需要重新定义测试中的"成功"与"失败"。传统上,大多数项目经理会把没发现错误的测试称为"成功的测试",而把发现新错误的测试称为"失败的测试"——这完全是颠倒的。正确的理解应该是:成功的测试是发现了可以修复的错误,或最终确认没有更多错误;失败的测试是那些没有真正有效检查软件、什么错误都没发现的测试(因为不存在完全没有错误的程序)。就像病人去看医生,做了一堆化验却没找到病因才是失败的,查出病因可以对症治疗才是成功的——我们应该把待测试的程序看作生病的病人。
常见的测试定义存在三大问题。首先,"测试是证明程序没有错误"这个目标对几乎所有程序来说都不可能实现,而心理学研究表明当人们做明知不可能完成的任务时表现通常很差;把测试定义为寻找程序中的错误会让它变成可行的任务。其次,"测试是证明程序实现了预期功能"忽略了一个事实:即使程序做了它该做的事,仍然可能存在错误——程序没做该做的事是错误,程序做了不该做的事同样是错误。例如三角形程序即便能正确区分三角形类型,但如果把1、2、3判定为不等边三角形,或把0、0、0判定为等边三角形,程序依然是错误的。第三,如果只是证明功能正确,很难发现程序"做了不该做的事"的错误。
因此,程序测试应该是一种破坏性过程:先假定程序存在错误,然后努力找出这些错误。成功的测试用例是能促使程序运行失败、推动我们发现问题的用例。当然,我们最终还是希望通过测试建立信心,确认程序做该做的事、不做不该做的事,但要达到这个目的,最好的方式是勤奋、彻底地去寻找错误。如果有人说"我的程序完美无缺",验证这句话最好的方式不是拿几组数据证明它能跑,而是尽力去推翻它——努力找出它的缺陷。记住:一个发现新错误的测试用例绝不是失败,相反它证明了自己是有价值的投入;失败的测试用例是那些只让程序输出正确结果、却没有发现任何问题的用例。
2.测试经济学:
给了软件测试的定义后,下一步是决定是否要找到测试一个程序发现的所有错误。我们将向你展示,答案是否定的,即使对于最简单的程序也是如此。总的来说,找出程序中的所有错误是不切实际的,往往是不可能的。这个根本性的问题反过来会影响测试的经济性、测试人员必须对程序做出的假设,以及测试用例的设计方式。
为了应对与测试经济性相关的挑战,你应该在开始之前建立一些策略。两种最常见的策略包括黑盒测试和白盒测试,我们将在接下来的两个章节中探讨这些内容。
3.黑盒测试:
一个重要的测试策略是黑盒测试(也称为数据驱动测试或输入/输出驱动测试)。使用这种方法时,将程序视为一个黑盒。你的目标是完全不关心程序的内部行为和结构,而是专注于找出程序不符合其规格说明的情况。
在这种方法中,测试数据完全从规格说明中推导出来(即不利用程序内部结构的知识)。
穷尽输入测试的困境
如果你想用这种方法找出程序中的所有错误,标准就是穷尽输入测试——将每个可能的输入条件都作为测试用例。为什么?因为如果你对三角形程序尝试了三个等边三角形测试用例,这并不能保证正确检测所有等边三角形。程序可能包含对值3842、3842、3842的特殊检查,并将这样的三角形标记为不等边三角形。由于程序是一个黑盒,确保检测到这种语句存在的唯一方法,就是尝试每个输入条件。
要穷尽测试三角形程序,你必须为开发语言最大整数大小范围内的所有有效三角形创建测试用例。这本身就是天文数字般的测试用例数量,但这绝不是穷尽的:它不会发现程序说-3、4、5是不等边三角形,以及2、A、2是等腰三角形的错误。要确保找到所有这类错误,你不仅要测试所有有效输入,还要测试所有可能的输入。因此,要穷尽测试三角形程序,你实际上需要产生无限数量的测试用例,这当然是不可能的。
更复杂的场景
如果这听起来很困难,那么对更大程序的穷尽输入测试就更成问题了。考虑尝试对C++编译器进行穷尽黑盒测试:
你不仅要创建代表所有有效C++程序的测试用例(同样是无限数量)
还要为所有无效的C++程序创建测试用例(无限数量),以确保编译器将它们检测为无效
也就是说,必须测试编译器以确保它不做它不应该做的事——例如成功编译一个语法错误的程序
对于基于事务的程序(如数据库应用程序),问题更加繁重。例如,在航空预订系统这样的数据库应用程序中,事务(如数据库查询或航班预订)的执行取决于之前事务中发生的情况。因此,你不仅要尝试所有独特的有效和无效事务,还要尝试所有可能的事务序列。
4.白盒测试
另一个测试策略是白盒测试(或称逻辑驱动测试),它允许你检查程序的内部结构。这种策略从检查程序的逻辑中推导测试数据(不幸的是,往往忽略了规格说明)。
此时的目标是为这种策略建立类似于黑盒方法中穷尽输入测试的标准。让程序中的每条语句至少执行一次似乎是答案,但不难证明这是非常不充分的。这里不赘述这一点,因为第4章将更深入地讨论这个问题。通常认为,类似的标准是穷尽路径测试。也就是说,如果你通过测试用例执行程序中所有可能的控制流路径,那么程序可能就被完全测试了。
穷尽路径测试的两大缺陷
缺陷1:路径数量天文数字般庞大
然而,这个说法有两个缺陷。第一个是程序中唯一逻辑路径的数量可能是天文数字般的庞大。为了说明这一点,考虑图2.1中表示的简单程序。该图是一个控制流图,每个节点或圆圈代表顺序执行的一段语句,可能以分支语句结束。每条边或弧代表段之间的控制转移(分支)。
该图描绘了一个10到20条语句的程序,包含一个最多迭代20次的DO循环。在DO循环体内是一组嵌套的IF语句。确定唯一逻辑路径的数量等同于确定从点a到点b移动的唯一方式总数(假设程序中的所有决策彼此独立)。
这个数字大约是1014,即100万亿。它由520 + 5^19 + ... + 5^1计算得出,其中5是通过循环体的路径数。
如何理解这个数字?
如果你每5分钟编写、执行并验证一个测试用例,尝试每条路径需要大约10亿年
如果你快300倍,每秒完成一个测试,你可以在320万年内完成任务
当然,在实际程序中,并非每个决策都独立于其他决策,这意味着可能的执行路径数量会少一些。另一方面,实际程序比图2.1中描绘的简单程序大得多。因此,穷尽路径测试与穷尽输入测试一样,看起来是不切实际的,甚至是不可能的。
缺陷2:即使测试所有路径,程序仍可能充满错误
"穷尽路径测试意味着完整测试"这一说法的第二个缺陷是:即使测试了程序中的每条路径,程序仍可能充满错误。有三个原因:
原因1:不能保证程序符合规格说明
穷尽路径测试无法保证程序符合其规格说明。例如,如果要求你编写一个升序排序例程,但你错误地生成了一个降序排序例程,穷尽路径测试将毫无价值;程序仍然有一个错误:它是错误的程序,因为它不符合规格说明。
原因2:无法检测缺失的路径
程序可能因为缺少路径而不正确。穷尽路径测试当然不会检测到必要路径的缺失。
原因3:无法发现数据敏感性错误
穷尽路径测试可能无法发现数据敏感性错误。例如,假设在程序中你需要比较两个数字的收敛性,即查看两个数字之间的差是否小于某个预定值。你可能会写一个Java IF语句:
if (a-b<c)
System.out.println("a-b<c");
当然,该语句包含错误,因为它应该将c与a-b的绝对值进行比较。然而,检测这个错误取决于a和b使用的值,不一定通过仅执行程序中的每条路径就能检测到。
5.软件测试准则

浙公网安备 33010602011771号