对于测试从业者来说,测试工作是一项技术活,但同时它也涉及到经济学和人类心理学一些重要因素。
在理想情况下,我们会测试程序的所有可能执行情况,而在大多数情况下,这几乎是不可能的。即使一个看起来非常简单的程序,其可能的输入与输出组合可达到数百种甚至数千种,对所有的可能情况都设计测试用例是不切合实际的。
对一个复杂的应用程序进行完全的测试,将耗费大量的时间和人力资源,这样在经济上是不可行的。
另外,要成功地测试一个软件应用程序,测试人员也需要有正确的态度,在某些情况下,测试人员的态度可能比实际的测试过程本身还要重要。
因此,在深入探讨软件测试的本质之前(指技术层面),我们先探讨一下软件测试的心理学和经济学问题。
测试需要了解的心理学
测试执行得差,其中一个主要原因在于大多数的程序员一开始就把“测试”这个术语的定义搞错了。他们可能会认为:
“软件测试就是证明软件不存在错误的过程。”
“软件测试的目的在于证明软件能够正确完成其预定的功能。”
“软件测试就是建立一个‘软件做了其应该做的’信心的过程。”
这些定义都是本末倒置的。
每当测试一个程序时,应当想到要为程序增加一些价值。通过测试来增加程序的价值,是指测试提高了程序的可靠性或质量。提高了程序的可靠性,是指找出并最终修改了程序的错误。
因此,不要只是为了证明程序能够正确运行而去测试程序;相反,应该一开始就假设程序中隐藏着错误(这种假设对于几乎所有的程序都成立),然后测试程序,发现尽可能多的错误。
那么,对于测试,更为合适的定义应该是:
“测试是为发现错误而执行程序的过程”。
虽然这看起来像是个微妙的文字游戏,但确实有重要的区别。理解软件测试的真正定义,会对成功地进行软件测试有很大的影响。
人类行为总是倾向于具有高度目标性,确立一个正确的目标有着重要的心理学影响。如果我们的目的是证明程序中不存在错误,那就会在潜意识中倾向于实现这个目标;也就是说,我们会倾向于选择可能较少导致程序失效的测试数据。
另一方面,如果我们的目标在于证明程序中存在错误,我们设计的测试数据就有可能更多地发现问题。与前一种方法相比,后一种方法会更多地增加程序的价值。
举例来说,它暗示了软件测试是一个破坏性的过程,甚至是一个“施虐”的过程,这就说明为什么大多数人都觉得它困难。
这种定义可能是违反我们愿望的;所幸的是,我们大多数人总是对生活充满建设性而不是破坏性的愿景。大多数人都本能地倾向于创造事物,而不是将事物破坏。这个定义还暗示了对于一个特定的程序,应该如何设计测试用例(测试数据)、哪些人应该而哪些人又不应该执行测试。
为增进对软件测试正确定义的理解,另一条途径是分析一下对“成功的”和“不成功的”这两个词的使用。当项目经理在归纳测试用例的结果时,尤其会用到这两个词。大多数的项目经理将没发现错误的测试用例称为一次“成功的测试”,而将发现了某个新错误的测试称为“不成功的测试”。
这又是一次本末倒置。
“不成功的”表示事情不遂人意或令人失望。我们认为,如果在测试某段程序时发现了错误,而且这些错误是可以修复的,就将这次合理设计并得到有效执行的测试称做是“成功的”。如果本次测试可以最终确定再无其他可查出的错误,同样也被称做是“成功的”。
所谓“不成功的”测试,仅指未能适当地对程序进行检查,在大多数情况下,未能找出错误的测试被认为是“不成功的”,这是因为认为软件中不包含错误的观点基本上是不切实际的。
能发现新错误的测试用例不太可能被认为是“不成功的”,也就是说,能发现错误就证明它是值得设计的。“不成功的”测试用例,会看到程序输出正确的结果而没发现任何错误。
我们可以类比一下病人看医生的情况,病人因为身体不舒服而去看医生。如果医生对病人进行了一些检查和化验,却没有诊断出任何病因,我们就不会认为这些检查和化验是“成功的”,因为病人支付了昂贵的检查和化验费用,而病状却依然如故。
病人会因此而质疑医生的诊断能力。但是,如果医生诊断出病人是胃溃疡,那么这次检测就是“成功的”,医生可以开始进行相应的治疗。因此,医疗行业会使用“成功的”或“不成功的”来表达诊断结果。我们当然可以类推到软件测试中来,当我们开始测试某个程序时,它就好似我们的病人。
“软件测试就是证明软件不存在错误的过程”,这个定义会带来第二个问题。对于几乎所有的程序而言,甚至是非常小的程序,这个目标实际上也是无法达到的。
心理学研究表明,当人们开始一项工作时,如果已经知道它是不可行的或无法实现时,人的表现就会相当糟糕。
诸如“软件测试就是证明‘软件做了其应该做的’的过程”此类的定义所带来的第三个问题是,程序即使能够完成预定的功能,也仍然可能隐藏错误。也就是说,当程序没有实现预期功能时,错误是清晰地显现出来的;如果程序做了其不应该做的,这同样是一个错误。
测试需要了解经济学
给出了软件测试的适当定义之后,下一步就是确定软件测试是否能够发现“所有”的错误。我们将证明答案是否定的,即使是规模很小的程序。一般说来,要发现程序中的所有错误也是不切实际的,常常也是不可能的。这个基本的问题反过来暗示出软件测试的经济学问题、测试人员对被测软件的期望,以及测试用例的设计方式。
为了应对测试经济学的挑战,应该在开始测试之前建立某些策略,黑盒测试和白盒测试是两种最普遍的策略。
黑盒测试
黑盒测试是一种重要的测试策略,又称为数据驱动的测试或输入/输出驱动的测试。使用这种测试方法时,将程序视为一个黑盒子。测试目标与程序的内部机制和结构完全无关,而是将重点集中放在发现程序不按其规范正确运行的环境条件。在这种方法中,测试数据完全来源于软件规范(换句话说,不需要去了解程序的内部结构)
如果程序使用到数据存储,如操作系统或数据库应用程序,这个问题会变得尤为严重。
不仅要测试所有有效的和无效的事务处理,还要测试所有可能的事务处理顺序。
由于穷举测试是不可能的,测试投入的目标在于通过有限的测试用例,最大限度地提高发现的问题的数量,以取得最好的测试效果。
白盒测试
另一种测试策略称为白盒测试或称逻辑驱动的测试,允许我们检查程序的内部结构。这种测试策略对程序的逻辑结构进行检查,从中获取测试数据(遗憾的是,常常忽略了程序的规范)。
当然,在实际程序中,判断并非都是彼此独立的,这意味着可能实际执行的路径数量要稍微少一些。
但是,从另一方面来讲,实际应用的程序要比下图所描述的简单程序复杂得多。因此,穷举路径测试就如同穷举输入测试,非但不可能,也是不切实际的。
“穷举路径测试即完全的测试”论断存在的第二个问题是,虽然我们可以测试到程序中的所有路径,但是程序可能仍然存在着错误。
即使是穷举路径测试也决不能保证程序符合其设计规范。
程序可能会因为缺少某些路径而存在问题。
穷举路径测试可能不会暴露数据敏感错误。
尽管穷举输入测试要强于穷举路径测试,但两者都不是有效的方法,因为这两种方法都不可行。
那么,也许存在别的方法,将黑盒测试和白盒测试的要素结合起来,形成一个合理但并不十分完美的测试策略。