测试现实主义的重要性
测试现实主义是一个奇怪的争议性原则,即自动化测试应该现实地进行测试。
这意味着,除其他外,*在可行的情况下*,自动化测试构建者应该力求
- 在密切模拟实际运行环境的环境中运行代码。
- 针对实际代码而不是模拟代码进行测试。
- 在使用模拟时,选择或构建现实的模拟。
- 优先编写模拟软件实际使用情况的测试场景。
- 在可能的情况下,使用生产数据库中的实际数据来编写测试,而不是合成数据。
这难道不是显而易见的吗?
虽然“现实地测试”可能*看起来*非常明显,但不幸的是,它与某些更流行的原则相冲突,主要是“FIRST”。
推向极端,这意味着你会听到一些人说“TDD 测试套件应该在 10 秒内运行”,这意味着,除了某些非常具体的软件类型之外,这意味着你一定会编写和运行*非常*不现实的测试。
FIRST 代表快速、隔离、可重复、自我验证和及时。它是 Tim Ottinger 和 Brett Schuchert 为描述“单元测试”的理想品质而开发的一组原则 - 其理念是这些原则将构成你的自动化测试套件的主体。这些都是测试的良好品质,但它们通常比现实主义次要得多。
它*不*代表现实主义。那个品质 - 决定你的测试实际捕获多少 bug 的品质 - 甚至没有被提及。我毫不怀疑他们认为它值得称赞 - 他们显然只是不认为它*必要*。
测试现实主义和测试速度之间固有的权衡
虽然说一个快速但没有发现任何 bug 的测试是毫无意义的,这很老套,但它远不止于此。
快速、不现实的单元测试*确实*有用地捕获了 bug,这也是为什么用它们进行 TDD 会如此强大的一种方法。一个不现实的、快速的测试,可以提供有关*某些* bug 的反馈,仍然是有用的。
它们只是没有捕获*那么多* bug,而且它们“捕获”了*更多*误报。
然而,现实的测试通常比不现实的测试慢。有可能(尽管很罕见)使自动化测试*极其*现实,而且慢得不可管理,但实际上并没有比其速度为 1/100 的测试捕获更多 bug。
因此,*始终*需要在极其现实且慢得不可管理以及超快但不现实之间进行权衡。
它不一定是微妙的权衡。在速度和现实主义方面,经常会出现急剧递减的回报。
如果一个测试需要半毫秒,另一个测试需要 500 毫秒,那么对于更快的测试来说,实际上*没有*任何好处 - 就开发人员而言,两者都在一眨眼之间就完成了。
我们应该在哪里划线?
假设你有两个测试同一个功能的测试 - 一个测试会连接真实数据库,并使用 selenium 与真实 UI 交互。另一个则模拟 UI(通常称为“皮下”)和数据库。
一个需要 25 秒,另一个需要 25 毫秒 - 快了 1000 倍。
非现实的测试在以测试驱动的方式编写时,会给出即时的反馈,而现实的测试则可能需要 25 秒才能注册失败。
但是,现实的测试会捕获很多非现实的测试不会捕获的错误,并且在功能正常工作时,失败的可能性更小。在一个相对简单的 Web 应用程序中,你看到的绝大多数(如果不是全部)错误都是“集成错误” - 由于两个模块或软件组件之间的交互而出现的错误 - 例如
- 网页在一个浏览器中显示正常,而在另一个浏览器中显示不正常。
- 数据库返回错误的结果,因为它的调用方式错误。
- 数据库执行了错误的计算,因为调用方式错误。
任何这些错误都不会在模拟或存根数据库的非现实测试中被捕获 - 更糟糕的是,你可能还会最终将错误嵌入到模拟或存根 UI 和数据库的测试中,然后在测试实际应用程序时不得不撤销它们。
这意味着每个项目中的每个测试都应该是一个端到端测试吗?
不,虽然对于某些项目来说,如果它们是非现实的,也不一定是问题。hitchdev 中的每个项目都是以这种方式测试的。虽然它们更像是单元测试而不是端到端测试,但它们确实包围了整个项目。
一方面,平均 < 25 秒的端到端测试是一个非常好的默认方法。另一方面,有一些测试可以以同样真实的方式运行,并且同样容易,而且速度快得多,在那种情况下,确实有意义在更低级别编写更快的测试。
将测试移至更低级别的最佳时机是
- 只有一个模块具有清晰的抽象,并且与周围的模块(或模块)松散耦合。
- 不同的团队负责不同的模块(这就是可执行规范也派上用场的地方)。
我如何知道我的测试是否足够现实?
当然,绝对的现实是不可能的,测试现实的收益递减规律适用于测试现实,就像它适用于速度一样。