你在测试金字塔的哪一层(上)
在准备将软件上线到生产环境之前需要进行测试。随着软件测试方式日趋成熟,软件开发团队的测试也在取代大量手动测试,逐渐实现自动化测试。 通过自动化测试,开发团队可以在短短几分钟内就了解到软件是否存在问题,而不需要等待几天的时间。
自动化测试大大地缩短了反馈周期,与敏捷开发、持续集成和 DevOps 文化密切相关。本文将分为上、下篇来探讨如何构建一个高响应、可靠并且可维护的测试组合,无论是针对微服务架构、移动应用程序还是物联网生态系统。
一、自动化测试的重要性
早期,软件的目的仅仅是提高企业效率,但如今软件已成为我们生活中的重要组成部分。许多公司都在努力成为一流的数字化公司,而作为用户的我们每天都在使用各种各样的软件,创新的车轮转动越来越快。
要想跟上创新的脚步,我们必须在保证软件质量的同时加快交付的速度。持续交付是一种软件工程手法,通过在短周期内完成软件产品的交付过程,确保软件可以稳定、持续地发布。通过构建流水线自动化测试,自动将其部署到测试和生产环境中。
随着软件数量的不断增加,手动构建、测试和部署很快就会变得不切实际。如果我们不想把大量时间都花在重复性的手动测试上,那么自动化测试是前进的必由之路。
从构建、测试到部署、基础架构,自动化测试是不可或缺的。它不仅能够提高效率、减少错误,还能够让开发人员释放更多时间专注于创造性的工作。
传统的软件测试通常是手动操作完成的,包括将应用程序部署到测试环境中,然后执行黑盒测试,如点击用户界面检查是否有任何故障。这些测试通常是由测试脚本指定,以确保测试人员进行一致的检查。
手动测试的所有变更都是耗时、重复且乏味的。重复是枯燥的,而枯燥容易导致错误的出现,还会让测试人员随时产生“另谋高就”的想法。幸好,有一种方法可以解决这种重复性工作:自动化测试。
自动化测试会极大程度地改变软件开发人员的工作方式。一旦将这些测试自动化,测试人员就不再需要手动执行点击操作来检查软件是否仍能正常运行。通过自动化测试,可以轻松修改代码库。如果之前在没有适当测试组合的情况下进行大规模重构,你一定会知道这是多么可怕的经历。
如何确保在重构过程中避免不小心破坏任何内容?只能一个个手动执行测试用例了。如果能在喝一口咖啡的时间内,在几秒钟内知道自己是否进行了大规模修改,那该有多好。这听起来更有意思。
二、测试金字塔
在《敏捷成功》一书中,Mike Cohn 提出了一个重要的概念:测试金字塔。这个概念通过视觉隐喻向我们展示了不同层次的测试。
Mike Cohn 独创的测试金字塔由三层组成(从下到上):
单元测试
服务测试
UI 测试
然而,一些人对测试金字塔的命名或概念提出质疑。虽然测试金字塔确实过于简单,会产生某些误导,但在实际应用中,测试的层次和比例会因项目的特殊需求而有所不同。因此,我们需要灵活地应用测试金字塔,根据具体情况进行调整和定制,以确保我们能够全面而有效地测试软件。
尽管如此,测试金字塔的本质是一种经验法则,用于构建自己的测试组合。Cohn 强调在最初构建测试金字塔时要注意两点:
编写不同粒度的测试
随着测试级别的提高,应进行的测试数量会减少
坚持金字塔的形状,以构建一个健康、快速和可维护的测试组合,但不要形成“测试冰激凌锥”,因为这会导致维护困难且运行时间过长。
我们不必过于拘泥测试金字塔中每层的名称,这些名称可能会带来一些误导。例如,“服务测试”是一个难以理解术语,正如 Cohn 本人曾说的“我观察到很多开发人员完全忽略了这一层”。在现代的单页面应用框架(如 react、angular、ember.js)中,UI 测试显然不必位于金字塔的最高层,完全可以对 UI 进行单元测试。
考虑到原始名称的缺点,根据代码库和团队讨的需要,为测试金字塔每层选择其他名称,只要中保持一致即可。
三、注意事项
1、团队在测试命名上保持统一
我们很难去讨论测试的不同分类,不同的人对不同测试类型的理解存在着差异。术语含义本身有模糊性,在这个问题上并没有绝对的对与错。无论是端到端测试、广域栈测试,还是功能性测试,都没问题。
如果业界能有一些明确定义的术语并统一语言,那是再好不过了。迄今为止,软件开发社区也没法给出关于测试术语的明确定义。此外,在编写测试时会存在许多细微差别,它们的范围更像是互相重叠而不是互相独立的,这使得保持术语的一致性更为困难。
重要的是找到适合团队的术语,并清楚理解不同类别测试之间的区别。团队需要在测试命名上保持统一,并为每一类测试明确定义范围。只要在团队内部达成一致,就不需要过多关注其他事情了。
2、把测试放在部署流水线上
如果正在实施持续集成或持续交付的实践,那么在每次提交更改时,将使用一个部署流水线来运行自动化测试。这个流水线通常会被分成几个阶段,逐步建立起团队对将软件部署到生产环境的信心。在考虑如何在部署流水线中放置不同类型的测试时,需要思考持续交付的核心价值观之一:快速反馈。
构建流水线的目标是在构建失败时能够及时通知测试人员。测试人员肯定不希望等待一小时后才发现最新的改动因为几个简单的单元测试而失败。
为了快速获得反馈,我们可以将运行快速快的测试放在流水线的较早阶段执行,这样我们可以在迅速得到反馈。反之,将运行时间较长的测试(通常是覆盖范围更广的测试)放到流水线的后期阶段执行,以免影响我们从运行速度快的测试中获取快速反馈的体验。
部署流水线中不同阶段的差异并不是由测试类型决定的,而是取决于测试的运行速度和覆盖范围。 在这种情况下,将一些覆盖范围有限、运行速度快的集成测试与单元测试放在同一个阶段是一个合理的决策。我们的目标是更快地获得反馈,而不是在各种类型的测试之间划出清晰的界线。
3、避免测试重复
我们已经了解了为什么需要为软件编写不同类型的测试,但是这还有一个需要避开的陷阱:金字塔不同层级进行重复测试。编写和维护测试需要花费时间,而阅读和理解其他人编写的测试也是如此,此外运行这些测试也要费时间。
对于产品代码,我们应该追求间接性,尽量避免重复。在实现测试金字塔时,我们应该牢记以下两个基本法则:
当上层测试发现了一个错误,并且底层测试已经全部通过时,我们应该写一个更低层级的测试来覆盖这个错误。
我们应该尽可能地将测试推进到金字塔的下层。
第一条法则是因为通过低层级测试有助于缩小错误范围,并将大部分上下文隔离开,从而更容易重新调试错误。在解决当前问题时,低层级测试能够更快地运行,且没有太多冗余的内容。此外,它们也是很好的回归测试,确保已修复的问题不会再次出现。
第二条法则能保持测试组合的快速运行。如果在底层及测试中已经覆盖了所有情况,那么维护一个高层级的测试就没有必要了。因为它并不能为软件的正常工作提供更多的信心。如果有许多无效的测试,它们只会让你的日常工作变得繁琐。这样的测试组合会拖慢工作节奏,当你改变代码行为时,还需要修改更多的测试。
总而言之,如果编写更高层级的测试能增加对软件的信心,那么就编写高层级的测试。在处理 Controller 类时,单元测试可以用来测试其内部逻辑,但无法验证该 Controller 是否能够真正响应 REST 路径的 HTTP 请求。在这种情况下,我们可以通过编写一个专门测试这一点的测试来提升测试层级,只关注这一点而不需要涉及其他内容。我们无需重复测试所有条件分支和边缘场景,因为底层级测试已经涵盖了这些内容。确保高层级测试仅关注底层及测试未覆盖到的部分。这样可以确保测试的焦点准确,并避免重复劳动。
对待已经失去价值的测试,我们需要果断将其淘汰。哪些已经被低层级测试完全覆盖的高层级测试应该被删除,因为它们不再提供额外的价值。我们应该尽可能用低层级测试来取代高层级测试。尽管有时会面临一些挑战,特别是当我们意识到设计测试本身就很具有挑战性时,但我们要警惕陷入沉没成本的思维陷阱,果断摁下删除键。我们不要在在那些不再提供价值的测试上浪费宝贵时间。
四、写在最后
不管你是工作在一个微服务项目上,还是 IoT 设备上,抑或是手机应用或者网页应用,希望这篇文章能够为你提供帮助。下篇,我们将详细介绍测试金字塔的三个层级。
文章翻译来源:https://martinfowler.com/articles/practical-test-pyramid.html
版权声明: 本文为 InfoQ 作者【禅道项目管理】的原创文章。
原文链接:【http://xie.infoq.cn/article/cf15d66f8b113b4579f429a9e】。文章转载请联系作者。
评论