决定的内容总是具体的,因此无法预计;它总得由人去发明。

——让-保罗•萨特(Jean-Paul Sartre) ①

宏观问题

检验一个软件系统所需要的测试是不可穷尽的,因为我们不可能遍历客户的所有可能操作,或者说即使能够穷举这些可能的操作,并且我们在实验室所进行的测试与客户所做的现场操作步骤完全一样,但我们仍只能说这是不完全相同的两个场景,就像古希腊哲学家赫拉克利特(Heraclitus)的名言:人不能两次踏入同一条河流。场景的每一次执行既相同又不完全相同,而时空的不可穷尽则是测试证明的本质障碍。

我们既存在又不存在。

当认识到我们不可能证明一个无穷的场景集合时,软件测试就成了一门有关选择的科学:其核心内容便是研究如何从无穷集合中挑选一个有限的集合来对软件系统进行测试。在这里,我们将测试选择的这一根本问题视为宏观问题,并将在下文中展开更为详细地分析;而将关于测试我们知道什么以及不知道什么的问题,划归到哲学视角,这已在上一篇论文中得到了比较全面的探讨②。

测试的比较

我们必须承认的是,假如真能够从证明的角度来看待软件测试的话,那么一切与测试相关的理论无疑都将是纯粹的和精致的,同时其明确的结果也满足了人对于预期、秩序与简单的追求;但我们仍不得不放弃它,因为在我们有限的生命与有限的理性里,我们不大可能解决归纳与证明问题。也可以说,通过放弃对证明的奢求,我们因而得以回避了一个无法解决的问题,也不再被无穷集合所困扰。但是,这样的选择并非是没有代价的:一方面,与测试相关的理论变得不那么简洁和优雅,而是不可避免的略显晦涩;另一方面,当我们绕过证明这一不可征服的高山时,却与另一难题狭路相逢。

这一难题就是对于不完备的测试,我们如何比较两种测试设计——也就是两个不同测试子集的优劣。在将测试视为一种证明时,因为我们的目的是证明所有场景,所以除了完全集合,任何不完全子集都是不完整的。而如果比较的是两个不同子集,则只要子集内部不存在冗余,那么两个子集的比较便是简单的元素数比较。因此可以说,在证明的世界里,这一难题并不存在;但是,当我们从证明的世界里走出时,我们却不得不面对这一难题的纠缠。

同一个受测系统的两种测试设计哪一种更好,这并不是一个容易的问题,尽管我们可以教条式地回答说:两相比较,我们选择“以最少的测试检验出最多的缺陷”的那个。然而这个的原则本身就有诸多疑点,在Jorgensen 2002中,保罗•乔根森(Paul Jorgensen)认为测试有效性的最佳解释是最困难的:“首先,要假定我们知道程序中的所有缺陷。形成死循环的是,如果我们知道程序中的所有缺陷就会采取有针对性的措施。由于我们不知道程序中的所有缺陷,因此永远也不会知道给定方法所产生的测试用例能否发现这些缺陷”③。实际上这里的困难是,我们不能将最终检验出的缺陷数作为评价一个测试设计的标准。试想假如一个包含了1000个测试用例的测试设计,最终结果是只有其中1个测试用例检验出了1个缺陷,另外的999个测试用例均测试通过;而另一个测试设计就仅仅包含了这个检查出缺陷的测试用例,然后它也同样地检验出了1个缺陷,这时难道我们可以说后一测试设计比前者更好吗?当然,对于这个特定的软件实现(哪里有缺陷,哪里没缺陷)来说,后一测试设计的确更有效率,因为它发现了同样多的缺陷,却只用了相对于另一测试设计1/1000的投入,完全符合“以最少的测试用例检验出最多的缺陷”的标准。但这里的关键是,对测试设计的评价是不能依赖具体的软件实现的,某一测试用例是否有必要,不能以它最终有没有检验出缺陷来衡量。无论是否检验出缺陷,对于测试设计来说这都应该被视为一种偶然、一种对未知。在前面的这个例子里,我们可以这样认为:后一种测试设计它仅仅只是偶然地遇到了这么一个特定的软件实现——在这个软件实现里,正好在它检验了的那部分存在缺陷、同时也正好在它没检验的那部分表现正常。因此,我们没任何理由认为这样的一种“结果高效”的测试设计是优于其它测试设计的,就像我们不能因为有人购买一注彩票中奖就否定其它投注者的投注方式一样。对测试设计的评价不应该建立在任何特定的软件实现的基础上,因为任何特定的软件实现在这个不可能精确计算的世界里都应当被认为是偶然,而最终的缺陷分布情况也是偶然。也就是说,当我们以结果来评价两个测试设计时,如果一定要分出优劣,我们也只能说在某种特定的软件实现与缺陷分布下,一个测试设计偶然地高效于另一个测试设计。

当然,还有另一种说法,即评价测试设计的高效的标准是“以最少的测试覆盖到最多的缺陷”。随着从“检验出”到“覆盖到”的转变,“缺陷”一词的意义也随之变化了,这里的缺陷一词就特指潜在可能的缺陷,因而它不是结果论的,也由此避免了对特定软件实现与缺陷分布的依赖。但这一标准并不能给我们多大的指导意义,因为如果违背这一标准,则意味着对于同一缺陷我们加入了多余的测试。但是在什么情况会出现多余的测试呢?完全相同的测试肯定算是,但不完全相同的测试呢?比如对于一个接受1~255的输入,我们设计了5个测试:分别1、2、250、254、255,如果在此基础上,我们再加入一个测试用来测试输入3,那么这个测试算是多余的吗?在这里,除非我们能断定导致测试输入3可能失败的潜在缺陷与其它输入可能检验出的缺陷是源自同一错误,否则我们就不能将这个测试视为多余。但是很显然,我们并不能做出此类的断定,因为既然讨论的是潜在可能缺陷,那么我们所能断言的就只是其结果(缺陷的形式),而不是其原因(错误根源)。

因此,我们只能说对于测试设计效率的客观评估是困难的,不管是遵循什么样的标准。当我们试图比较两个测试设计时,如果以最终检验的缺陷数来衡量,那么这一评估也就仅仅只是对当前的软件实现是客观的,而对于其它可能的实现或缺陷分布来说则是未知的;另一方面,如果我们用覆盖的潜在缺陷来衡量,那么这似乎又是另一种形式的倒退,因为我们必须对两个缺陷是否源自同样的错误做出断言,但我们也知道,这一过程既是不可能的、同样也是不充分的。

测试的模式

选择的存在,一方面赋予了软件测试以新的核心与意义,标志着测试不仅仅只是执行检测,其更为重要的内涵是做出决策;但另一方面它也使得测试工作者不得不面对着新的困难:即如何做出比较与选择的问题。在测试学科数十年的发展历程中,人们发明了各种各样的测试方法,这些测试方法所基于的假设与侧重点并不一样,但目的都是为测试工作者的选择提供一定的指导。

在这些方法中,无论是功能性测试还是结构性测试,本质上都是关于选择的。在功能性测试中,边界值方法指导我们在边界与非边界中作出选择(例如在测试1~255时,我们选择输入1、2,而没有选择3);在等价类方法中,我们实际上也是通过划分互不相交的一组子集,然后从子集中选择一个元素作为测试输入;决策表方法同样是关于选择的,满足一个规则的变量可能有许多,但最终我们只会选择其中一个。而对于结构性测试而言,尽管在方法的立足点与技巧上相异甚远,但是结构性测试方法的本质上仍然还是选择:因为使得程序在某一特定路径上执行的选择大多数时候并非一个。

因此,我们可以说测试方法简化了测试选择,或者说,这些测试方法为我们所遇到的特定的选择问题提供了成熟的解决方案。比如边界值方法指导我们在面对一组输入范围时,如何选择测试用例,以更高效地找出系统里最可能出现的与边界相关的错误;但这一方法同时又对布尔变量和大部分GUI操作无解。其它测试方法也是如此,实际上每一种测试方法既有其擅长解决的问题,也有其不适宜的场景。比如,当我们比较功能性测试与结构性测试时,我们就发现:功能测试揭示不了软件实现了未被描述的行为,而结构性测试则发现不了软件未实现的被描述的行为。因此,我们可以把测试方法看作是解决特定问题的捷径,这些捷径的存在使得我们在面对这些特定问题时能快速做出成熟的选择,而不需要完全从头开始思考。建筑学家克里斯托夫•亚历山大(Christopher Alexander)作为模式与模式语言的奠基人,他对模式有如下的定义:“首先描述在我们的环境中反复出现的问题,然后给出该问题的核心解决方法,以这样的方式,你可以上百万次地使用这种解决方法”④,因而我们可以认为模式就是对一类特定问题的抽象与简化,并给出解决方案。正如建筑领域的建筑模式与软件设计中的设计模式一样,我认为测试方法就是软件测试领域里的模式。这类模式的存在,使得测试工作者并不需要彻底弄懂边界值测试方法的原理(不小于可能会误写为大于、不大于可能会误写为小于),也能设计出清晰的一致的测试用例集。就像掌握设计模式后,一些新人也能写出优雅且极具扩展性的设计一样。 相比起深层次理解要解决的问题,应用已有的模式无疑更为简单;同时,由于模式是建立在前人的经验上,应用模式显然能避免无谓的错误。因此我们可以这样理解:模式是一种具备了高度的简单性与准确性的理论,但它并不具备足够的普遍性,因为模式原本就是站在普遍性的对面,它针对的就是某类特定的问题。或者我们可以这样理解,模式牺牲了一定的普遍性,换取了高度的简单性与准确性。这就是沃伦•桑纳葛特(Thorngate 1976)所提出的相对称的复杂性原理⑤:一个社会理论通常无法同时具备普遍性、准确性以及简单性。当我们试图将简单性与准确性收入囊中的时候,那我们就自动地牺牲了普遍性。反之亦然,当我们说好的测试设计就是“用最少的测试用例检验出最多的缺陷”时,实际上我们是陈述了一个普遍的、简单的观点,因而当我们发现这个观点并不那么准确时,我们也不应该感到意外。

然而,在当今各个领域的研究中所存在的诸多问题,其根源多为不愿接受前文中所提及的不可避免的折中选择,研究者们表现得似乎可以在他们的方法或理论中同时兼顾这三个目标,实现鱼与熊掌兼得。甚至在那些获奖的项目管理书籍中(比如Rothman 2007),我们也能看到一些看似有理并且言之凿凿的论点,可是只要进一步思考这些论点,我们就不难发现它们要么没有断言任何确切的因果关系,要么断言了因果关系却不够精确。我们必须认识到,软件项目自是千差万别,而软件团队也是百态纷呈的,再加上技术变革的层出不穷,在这么一个复杂的领域里,要想提出一个既简单清晰、又准确无误,同时还能放之四海皆准的全新理论无疑是极为困难的。我们必须知道我们想要的是什么,以及为了得到想要的我们必须牺牲什么。

就测试方法而言,尽管没有哪种测试方法能适应所有的问题,但它们所提供的简单性与准确性依旧是宝贵的,而且也将永远都是我们在测试设计过程中所能依赖的最重要的工具。然而,借助测试方法这一工具,我们并不能回答为何如此设计测试的问题。方法不是选择的原理,因为方法自身也需要原理。因此,当我们决意探究有关测试设计的普遍性原理时,我们就必须超越易于理解的方法论,同时我们也必须作好牺牲简单性的准备——假如我们并不打算放弃准确性的话。

测试的选择

如果不存在匮乏,就不存在选择。如果测试可以零成本地执行,既不需要人力资源成本,也不需要时间成本,那么测试选择将没任何意义。但是正因为人力与时间均是有成本的,所以我们才有需要在不可穷尽的可能测试中做出选择,选择其中一部分而放弃余下的;选择值得测试的,而放弃不值得测试的。在这里使用的“值得”一词,实际上与我们常说的投入回报比,或者经济学中的边际收益—边际成本是一个意思,而边际收益—边际成本分析工具仍然是现代选择逻辑的核心部分。因此,涉及到有关测试的选择:选择一部分场景而放弃另一部分场景,选择某一个场景而放弃另一个场景,最终都可还原成对收益与成本的综合评估。

在通常语境下,如果我们说测试某一个场景的收益大于另一场景的收益,这意味着什么呢?如果考虑到测试是一种批判,那么我们显然应该选择更有可能检测出问题的场景,比如在测试1~255的输入范围时,我们在1与2之间选择1,这是因为我们认为相比起忘记累加,程序员更容易犯将不大于写成小于的错误。而当我们在各种环境配置中进行选择,测试某一种而不去测试另一种时,也是因为我们判断这样的选择相对而言会更有可能检测出缺陷,或者说至少也是同等可能性的。另一方面,当我们判断两种场景都有同样的可能性检测出缺陷时,选择可以是随意的。比如我们就不会觉得选择测试输入10与11会有什么本质上的差别。但是,当两个场景具有不同的重要性——也就是说一个场景的故障后果可能严重于另一个故障时,即使我们认为两者具有同等的可能性,我们也会倾向于选择更为重要的那个场景。根据这个原则,我们往往不会选择测试最终用户不会使用或较少使用的环境配置,而在选择测试主要场景还是扩展场景之时,答案也不言而喻的,即使一般来说扩展场景似乎更有可能检测出缺陷。因此,在比较的两个场景的测试收益时,我们将可能检测出缺陷的概率,以及缺陷的严重程度为依据做出评估:

测试收益 = 检测出缺陷的可能性 ╳ 检测出缺陷所能避免的损失

上述公式已能帮助我们对测试选择形成一个比较直观的认识了:在检测出缺陷的可能性相等的情况下,选择故障后果更严重的场景将收益更大;而在两个故障后果差不多的场景之间,公式指引我们选择更容易检测出缺陷的那个。在这两个变量的共同影响下,我可以认为,每一个测试都对应着一个特定的测试收益值。如果要在两个可能的测试中做出选择,那么在其它条件都相同的前提下,我们选择收益最大的那个;当两者的收益无差异时,如何选择也将是无差异的。

当然,测试收益并非是我们评估的唯一要素,对于测试选择来说,它必要但并不充分;因为除了测试收益外,还有另外一个不可忽视的因素,那就是测试成本,正如前文所说的,测试不可能零成本地进行。一般来说,测试成本相对而言比较好理解,因为通常我们将其解释为人月、人天或人时,视之为一种可度量的资源投入。在加入了测试成本这一因素后,我们的分析将更为全面,从效率的角度看,我们希望所选择的测试收益尽可能地大,同时所付出的成本又尽可能地小。因而,我们有可能会在评估可能的测试成本后,放弃掉一些收益可观的测试。在这种情况下,并非是该测试收益不够大,而是因为这一收益在更大的测试成本面前,显得并不值得。测试是一个获取信息的过程,测试能让我们知道得更多,但在有些时候,信息的成本有可能会大于无知的成本。

综合测试收益与测试成本,我们将测试选择演变成了一个关于效率的问题,也就是说我们将选择更有效率的测试,并且希望接下来的每一单位测试成本的投入所产生的收益能够最大化。在进行测试设计时,测试工作者实际上面临的是一个无比巨大的测试集合(因为有无数的可能场景),我们从零开始,不断地从集合中挑选出能让接下来一单位的测试成本投入产生出最大收益的测试用例,之后一直重复这一过程,直到在原集合中再也找不出单位收益能大于单位成本的测试用例。

测试成本与收益

由于我们总是先选择现存的、能令单位成本收益最大化的测试,因此随着选择的持续与投入的增加,单位成本的收益(dr/dc)必将递减,最终会小于1,并从上方穿过成本收益相等线(dr/dc=1)。这是因为,总会有一些场景是收益小于回报的,此时dr/dc<1。因此,最佳测试投入的选择就在于收益等于回报的那一点,在这一点上,继续往前是得不偿失的,而后退一点则是未充分最大化收益。上述过程描述了我们对测试用例的理性选择,如同测试场景的不可穷尽论指出完备的测试是不可能的一样,测试的效率论认为:完备的测试不仅是不可能的,同时也是不经济的。

测试的评估

在上文中,我们在尽量避免提出这样一种简单的观点:即对测试的选择,实际上就是选择一切测试收益大于执行成本的测试。从结果上看,这一观点与我们前面的讨论似乎是一致的,两者最终都包含了所有收益大于成本的测试,也都排除了一切得不偿失的场景。但是它们之间存在着一个不明显但非常关键的差异:在这个简单的观点中,其核心前提是测试的收益与成本是明确的,同时也是固定的;但在前面的持续选择理论里,却并不要求有这样的前提。尽管将测试的成本与收益看作是明确的与不变的——这一点既符合我们的直觉也吻合了我们的期望,但是这并不是现实。在现实中,测试的收益既不是客观明确的,也不是持续不变的。我们以边界值选择为例,在一个边界值测试方法中,我们通常会选择min、min+、nom、max-、max这五种场景,其中nom项是随意的,而其它四项是明确的。假如在1~255的测试中,我们可以将200作为nom项,根据“选择一切收益大于成本的测试”的观点,我们之所以选择输入200这一场景,必定是因为测试它的收益大于其执行成本。但这样一来,问题就出现了:我们既然认为测试输入200是有效率的,并将其纳入我们的选择集中,那么我们又该如何解释像输入199、输入201这些看上去与输入200并无差异但却被排除在外的场景呢?因为我们没有任何理由认为它们在测试收益与执行成本上与输入200有何不同。

因此,我们有必要将测试的选择看作是一个持续和动态的过程,在这一过程中,每一个测试的收益都不会是固定和绝对的。它之所以不是绝对的,是因为测试的收益通常不会孤立地存在,它取决于我们已经选择了哪些测试。同时这一收益也只存在于选择前,并且随着我们选择的完成而变得模糊。比如一个可以在4个浏览器上执行的测试用例,当我们在这4个可能的测试场景中做出选择前,每一个场景都有它的收益值,这个收益值可能因具体浏览器的市场份额不同而不同,此时的收益值是当我们只打算从中选择一个场景时的测试收益。然而一旦我们选择了其中一个测试场景后,比如用户数最多的那个,那么受已选择的这个场景影响,其它场景的收益值必将有一定程度的降低。因为场景的测试收益部分地取决于发现新问题的可能性,而当测试用例已在某一环境中执行后,我们在另一类似的环境中再执行这个测试用例时能发现新问题的可能性已经大大降低,因此收益值必将小于还未选择任何类似场景时的收益值。在边界值问题中也是如此:在我们没选择任何一个nom值时,所有的非边界值都是收益大于成本的,然而一旦我们选择了一个nom值后,原本还值得测试的那些非边界全部变得得不偿失起来,因为我们很难从它们身上测试出已选的nom所不能测试出的新问题来。因此,测试的收益不是固定不变的,在测试的选择这一持续的过程中,随着每一次选择的完成,未被选择的测试的收益将被重新赋值,而被选择的测试的收益也会随决策过程的进行而失去其独立性,变成总收益不可分割的一部分。

当然,测试的收益也不可能是客观明确的。尽管我们在前面已经建立了一个确定的收益公式,但事实上,无论是收益的价值还是收益的可能性,都是建立在预期的基础之上,它必然是一个向前看的或者事前的概念,因而是不确定的。在面临不确定性时,实际决策者对于测试收益的评价可能会不同于任何外部观察者。决策者必须为外部事件赋予主观概率,而这里不存在任何可加以客观决定的概率系数;同时,由于面对的是并不重复的单一事件,因而也没有任何的统计系数可供利用。在这里,我们所运用的数学、所建立的公式,实际上仅仅只是一种直观的语言、一种简洁的表达方式,目的只是用来说明收益评估过程中的关键因果关系。我们对收益的理解和评估,也并不会因为用了数学而更准确或者发现更多。尝试对单一事件的可能性进行“科学的”评估,实际上在许多学科的历史上都有误用这种或然率计算法的记录,这类情形正如约翰•穆勒(John Mill)所说,使其成为了“数学真正的耻辱” ⑥。可以这样认为,由于存在不确定性,我们对收益的价值评价或多或少都是基于主观的,而这一收益在未来是否发生也是或然的,这种主观性并不会因为我们使用公式表达而变得客观,而其发生的或然性也不会因为数学计算的使用而得以消除。正如诺贝尔经济学奖得主詹姆斯•布坎南(James Buchanan)所指出的:“在一个完全确定的世界里,不存在决策问题,即便真有‘选择’存在的话,那么一台计算机就完全可以全然处理。只有在不确定的世界中,才会有真正的选择”⑦。而测试选择就是这样的一种选择。

这些不同的因素都强调了这样一个事实:对测试的选择不存在任何简单的、准确的,同时又是普遍的方法,能让测试工作者可以一学便会、一蹴而就。相反,测试的选择是一个复杂的、主观的过程,我们必须持续地进行的取舍抉择,并且在每一个选择点上,既要前瞻又要后顾,既要分析又要猜测。然而,也正是这些不确定性与复杂性的存在、正是选择的智慧与勇气在这里的不可或缺,使得测试既是科学,又是艺术。

测试的成本

测试的收益与成本共同决定了最终的测试选择,在上文中我们已经完成了对测试收益全方位地探讨,而对于测试成本,我们仅仅只是暂时地将其视为资源投入的成本,这样处理一方面是与人的直觉相符,另一方面也是为了更专注地讨论收益问题。当我们把测试成本用人月、人日或者人时来衡量时,我们实际上是把测试成本看成一种量化的会计成本,一个可以事前预测、事后核算的数字。同时,也意味着我们还将其定义成了一种独立于收益的变量,既不影响收益也不为收益所影响。

然而,成本的概念并没有这样简单,尤其是它与收益之间,存在着某些不同于表面的微妙关系。在经济学中,海狸与鹿的故事有着极高的出现频率:“如果在一个以狩猎为生的国度里,捕杀一只海狸耗费的劳动通常两倍于捕杀一头鹿所耗费的劳动,那么一只海狸自然就应当交换两头鹿,或者说值两头鹿”⑧。上述论断来自亚当•斯密(Adam Smith),这也概括了古典的交换理论。正常的或自然的交换价值(也就是收益)由相对生产成本(海狸与鹿2:1)决定,这回答了古典经济学的核心内容。当然,这一理论是有缺陷的,至少从测试成本的角度来看,我们不能因为测试A场景的投入两倍于B场景,就说测试场景A的收益就应当两倍于场景B,这显然是谬论。然而,我们却可以说,海狸的成本是鹿而鹿的成本是海狸,或者说一个测试的成本就是因执行它而放弃的另一个测试的可能收益,在弗兰克•奈特(Frank Knight)看来,这一理解是“成本概念中唯一客观和科学的内容”⑨。一个商品的成本由可替代的或者舍弃的产品价值来衡量,这就是机会成本思想。因此,可以这样认为,海狸的成本是2头鹿,而鹿的成本是1/2头海狸,如果海狸的市场价值刚好是2头鹿的话,那么是选择捕杀海狸还是捕杀鹿并没有区别。但如果海狸的市场价值小于2头鹿,那么人们就不会选择捕杀海狸了,因为这样的选择是收益(1头海狸)小于成本(2头鹿)的。

同样,基于机会成本的思想,当银行存款年利是5%时,一笔投入100元、一年后收到101元的投资实际上就是不值得的,因为这一收益所面对的成本并非表面上的会计成本100元,而是选择了这一投资而放弃的潜在收益105元(机会成本)。因此,在测试选择中,我们倘若将成本简单地理解成资源的投入便有可能做出错误的选择,因为成本实际上是做出当前选择而必须放弃的其它选择的收益。也就是说,当一个人说一种特定的测试是“不值得”的,这并不是说其收益相比起对应的资源投入不值得,而仅仅只是意味着他偏好另外一个测试——如果选择另一个测试,收益将会更多。

成本就是我们所放弃的收益,我们在测试选择这一持续的过程中,不断地做出选择——选择收益大于机会成本的测试,一直到最后一个收益大于机会成本的测试。因为机会成本就是另一可能选择的收益,因此我们又可以将这一过程视为收益与收益之间的比较过程,成本的概念因之淡化,而仅仅是为比较两个选择的收益充当桥梁的作用。这一过程的最终结果,与我们在一系列选择中做出利润最大化的选择结果在大多时候是一致的,因此罗纳德•科斯(Ronald Coase)认为:“弥补成本和使利润最大化实际上是表达同一个现象的两种方式”⑩。但是,机会成本的思想能使我们站得更高、也看得更远,使得我们可以超出当前项目,看到更为宽广、更为全局化的选择。因为从机会成本的角度看来,将资源投入重新配置到另一项目或者另一类型的投资也是一种可能的选择。这样,我们就有可能正确地拒绝那些在当前项目看来有效率、但在全局上却并非最好选择的方案。很明显,在现实中,我们争论对某一个软件的测试是过多还是过少时、争论某一测试是必要还是没必要时,往往都是陷入了会计成本的误区。

小结

在软件测试领域里,不存在简单的问题。尤其是当我们不满足于简单方法论,试图寻求一种更普遍更一般化的选择理论时,各因素的不确定性与因果关系的复杂性便随之浮现出来。而对于这些不确定性与复杂性来说,它们既不可能被完全量化,也不可能被彻底消除。我们所能做的,仅仅只是适应与管理这些不确定性与复杂性,而不是假装其不存在。因为我们知道,引入数学和公式并不会从根本上使问题变简单,收益公式中有关或然率的计算,在某种意义上仅仅只是借助数学符号来表示这一有缺陷的知识。它既不扩张、也不拓深,更不补充我们的知识,它只是把这些已知的知识转变成数学语言。就像经济学家路德维希•米塞斯(Ludwig Mises)所说:“在评价、选择和行动中决没有什么可度量和可相等的,有的只是等级之差,也即取或舍”⑾。

经过前面的分析,现在我们应该可以轻松回答Paul Jorgensen在其经典著作中所提出的“什么时候测试可以停止”的问题了。答案的关键就在于机会成本,机会成本也是一切选择型问题的核心思想。然而,我们也必须清醒地认识到:我们仅能明确地回答“什么时候测试可以停止”,这是一个宏观的、实证的陈述;但我们无法明确地回答“什么时候某一软件的测试可以停止”,因为这涉及到有关收益的主观判断,因而不可能存在完全客观的答案。

注释

1. Jean-Paul Sartre 《Existentialism is a Humanism》. 1946.

[2]. 见前一篇《软件测试:哲学视角》.

[3]. Paul Jorgensen. 《Software Testing: A Craftsman’s Approach》. 2002.

[4]. Christopher Alexander. 《A Pattern Language》. 1977.

[5]. Warren Thorngate. 《"In general" vs. "it depends"》. 1976

[6]. John Stuart Mill. 《A System of Logic Ratiocinative and Inductive》. 1936.

[7]. James Buchanan. 《Cost and Choice》. 1999.

[8]. Adam Smith. 《The Wealth of Nations》. 1937.

[9]. Frank Knight. 《A Suggestion for Simplifying the Statement of the General Theory of Price》. 1928

[10]. Ronald Coase. 《Business Organization and the Accountant》. 1938

[11]. Ludwig Mises. 《Human Action: A Treatise On Economics》. 1949