(在我的同事 Doug Hoffman、Michael Bolton、Ken Pugh、Cem Kaner、Bret Pettichord、Jim Batterson 和 Geoff Sutton 以及许多参加过“雷区辩论”的学生的帮助下,作为我测试课的一部分。这个 "雷区 "比喻的灵感来自Brian Marick的演讲 《经典测试错误》)
在测试中查找bug就像在雷区中寻找地雷。如果你只是一遍又一遍地在雷区中走同一条路,你就不会发现很多地雷。事实上,这是避开地雷的好方法。现代软件产品所代表的空间比雷区复杂得多,因此,假设一些少量的“路径”,比如100条、1000条或100万条,在无止境地重复时,就能够找到每一个重要的bug,这就更成问题了。尽管一个测试团队在几周或几个月内能够执行的测试次数与产品在现场可能发生的所有情况相比,仍然不算多。
雷区的比喻实际上是另一种说法,即测试是一个取样过程,我们可能希望有一个更大的样本,具有良好的多样性,而不是一个微小的、特殊的样本不断重复。因此,基本的雷区启发式就是做不同的测试,而不是重复相同的测试。
但我说的重复同样的测试是什么意思呢?不难看出,任何测试都无法完全重复,就像你无法精确到微米级地追溯你的脚步一样。你可以接近,但总是会有一点点偏差。重复测试是否意味着第二次运行测试时,必须确保阳光以同样的角度照射到鼠标垫上?也许是这样的。我曾经遇到过一个 “bug”,就是由阳光照射到鼠标内部的光学传感器而触发的。你无法确定哪些因素会影响测试。不过,当你进行测试时,你有一个特定的目标和系统的特定理论。你可以根据目标和理论在以下方面重复测试:a)你知道的;B)你关心的;C)重复测试成本不高。这并不一定是一个棘手的问题。
因此,我所说的 “重复测试 “是指 “重复测试中重要的部分”。我认为这才是人们所说的 “重复”。重复某些事情是可能的。重复一切则不可能。然而,”雷区启发式 “建议,即便按照这个定义,也不要重复测试。
如果你不同意这个观点,或者你同意这个观点,请继续阅读。因为………这种分析过于简单了。尽管测试的多样性是重要且强大有力的,反对重复测试的论点通常来说是有帮助的,但我确实知道有十种例外情况。在某些特定情况下,重复测试并非不合理,这有十个具体原因。重复某些测试甚至可能很重要。
你可能会理性地重复测试的技术原因
- 回归:如果被测试产品(包括底层平台在内的任何层)发生了变化,这样就有很大的可能性出现新问题或重复出现的旧问题,而现有的特定测试就可以捕捉到这些问题。这包括重新运行测试以验证修复,或在连续的早期版本上重复测试,以发现特定问题或行为是何时引入的。这还包括在新O/S上相同软件上运行的旧测试。换句话说,测试的技术变化会给旧测试注入新的活力。请注意,”回归参数并不一定意味着你应该运行相同的旧测试,只是说这样做并不一定不合理。
- 间歇性:如果你怀疑一次测试的正确运行并不能保证发现错误,可能是因为测试中涉及的一些重要变量是你无法控制的。对你来说,执行一个与以前执行过的测试完全相同的测试,可能会发现一个一直存在的bug,但直到不可控变量以某种方式出现时才被发现。这与老虎机赌徒在第一次输了之后再玩一次的道理是一样的。
- 重试:如果你不确定在执行测试的其他时间是否正确运行,或者第一次测试时忽略了本可以收集到的信息。这就是为什么让多个测试人员按照相同的指令进行测试并检查他们是否都得到了相同的结果是一个好主意。这也是开发人员可能想要重现已报告的bug的常见原因。
- 突变:如果你在重复测试的另一个部分的同时,改变测试的一个重要部分。尽管你正在重复测试的某些元素,但是测试作为一个整体是新的,可能揭示新的行为。我之所以对测试进行突变,是因为虽然我之前已经涉及过某些内容,但还不够完善。一种常见的突变形式是使用不同的数据,以相同的方式操作产品。突变测试与间歇测试或重试测试的主要区别在于,突变测试中的变化是由您直接控制的。突变是有意为之,间歇是偶然因素造成的,而重新进行测试主要是因为偶然因素。
- 基准:如果测试的输出包含一个标准,而该标准的值是通过与以前执行的相同测试进行比较而获得的。最明显的例子就是性能基准测试。当历史测试数据被用作oracle时,必须注意所执行的测试与历史数据具有可比性。保持测试常量可能不是使结果具有可比性的唯一方法,但可能是可用的最佳选择。。
你可能会理性地重复测试的业务原因
(这些原因并不是单独起作用的,而是与技术原因结合在一起,使其更完善)。
- 重要性:通过这些测试发现的问题可能比其他测试发现的问题更重要。产品行为的重要性分布不一定是统一的。有时,一个特定的问题可能被认为是不可容忍的,因为它已经影响了一个重要的用户一次(一种“绝不让它再次发生”的情况)。这并不一定意味着你必须运行完全相同的测试,只需运行一些足够相似的测试来发现问题(请参考突变)。注意不要混淆问题的重要性和测试的重要性。可能有更好的测试可以检测到某个特定的重要问题,也许你不应该重复这个测试。
- 价格低廉:如果它们有一定的价值,而且与新的和不同的测试的成本足够低廉。不过,这些检测可能不足以覆盖产品。
- 充分:如果你重复的测试是唯一值得做的测试。然而,我们通常没有充分的理由认为我们得到了正确的测试集。我们可能会引入变化,因为我们不知道哪些测试真正值得做。
强制:如果由于合同、管理条例或法规的原因,您不得不运行完全相同的测试。然而,即使在这种情况下,通常也没有必要将强制测试作为你执行的唯一测试。你可以在不违反规定的情况下运行新的测试。
无差别/避免:如果运行 “测试 “的目的不是为了寻找bug,而是出于某种原因,如培训目的、演示目的(如您迫切希望在客户观看时能通过的验收测试),或使系统进入某种状态。如果运行测试的目的之一是为了避免bug,那么变量的主要论据就不复存在了。
我在与参加测试的学生和同事进行了大约100个小时的辩论之后,收集了这些原因。我的许多同事喜欢不同的词或不同的原因细分。我的做法并没有什么特别的地方(除了有些细分会导致一长串非常相似的项目)。重要的是,当我听到一个理由似乎与我已有的理由不符时,我就会把这个理由添加到这个列表中。我从 1997 年的两个理由开始,到2004 年末,我已经添加到了第十个理由。
应用雷区: 举例说明
Ward Cunningham写道:”我认为 TDD [测试驱动设计](和 Fit)所要求的自动化不会受到这种类比的影响,因为我们所做的搜索是为了在有测试的情况下寻找程序的最佳表达,而不是最佳测试。
以下是我的想法: 首先,我不把你写的代码称为 “测试”。测试是一个事件,是测试的一个实例。测试是一个人为的过程。就像所有想把工作做好的工匠一样,我需要清晰的语言和思想——在这种情况下,我们才不会忽视测试人员的角色。你指的是一组输出检查。这里的 “测试 “是设计或重新设计整个检查、执行这些检查并评估输出结果的过程。这样编写的话,当某些有趣的期望被违反时,它们就会失效。
你是否在重复这项测试?你只设计了一组检查,但却执行了很多次检查,而且每次都要对它们进行评估。这就是重复。你的评估过程会随着你的改变而发生一些变化,你也可能会有目的地修改这个测试,因为检查失败的原因不仅仅是发现了产品bug。但是,在运行过程中,很多事情都会被重复。
我们会在首次运行单元检查套件中的任何特定检查时引入雷区批评。一次运行时,它失败了,对吧? 当然,否则这就不是 TDD 了。现在,让我们来看看情况。
问:为什么要再次运行?
答: 回归。你再次运行它,是因为你已经添加了代码以使检查通过,因此再次运行测试不仅仅是冗余的,而且测试的价值已经被产品更改所补充。
问: 在开发过程中,但在第一次通过测试后,为什么不删除它?为什么还要再运行一次呢?
答:有几个原因。回归仍然适用,因为您可能会在开发过程中不小心破坏产品,但可以说,大多数单元测试在大多数情况下都不会失败,而且即使您对代码进行了大量修改,其中一些单元测试也不太可能失败。但还有第二个原因:价格便宜。创建这些检查、运行这些检查并保持它们运行的成本非常低廉,而与此同时,它们确实具有一定的价值,即便不多。有些检查还有第三个原因: 重要性。对于许多单元测试来说,失败将意味着一个非常严重的问题。如果你正在测试的东西特别复杂,或涉及许多相互影响的子系统,你也可能会因为间歇性而想要重复测试。也许由于测试中的概率因素,某些测试在运行 43 次后就会失败。最后,还有重试的原因,它提醒我们之前可能没有正确地运行测试。只有在你执行了一百次左右的测试后,有些事情才会困扰你。
问: 假设我是一个非常优秀的开发人员,虽然我编写了很好的检查,但它们并没有失败,因为我只是没有在代码中加入错误。我有大量的测试,但它们都没有失败。投入这些测试有什么意义?
答:有两个潜在的原因。一个是避免/无差别。你可以为未来的开发人员创建一种形式的检查文档,你希望它们完全相同,以便最大限度地减少它们失败的机会(从而减少作为文档的用处)。或者,你想用自己的优秀软件打动客户,但是如果没有通过检查,他们就不会对你留下深刻印象。第二个原因是强制性的: 你这样工作可能是因为你的同事或经理要求你这样做。这有点像回避,但实际上,你需要找到bug。你正在寻找它们,只是被要求使用某种技术来完成。
因此,我们可以看到,TDD中相当简单的、经常重复的单元检查可能确实不受雷区理论的影响,因为我提到的原因适用于改变测试。但是TDD也不能幸免于这种启发式分析。质疑反复试验的价值总是合理的,而这正是雷区邀请我们去做的。