前言:本文译自 Google Blog《How Much Testing is Enough?》,多少测试才足够?这个问题无法定论,针对不同的产品类型和特性,测试的度和策略都不尽相同。作者通过介绍一些测试的指引和方法论,从质和量两个角度给出了一些建议。
每个软件开发人员和团队都在努力解决的一个熟悉的问题是:“多少测试才足以使软件发布版本质量合格?”。这个问题在很大程度上取决于软件的类型、用途和目标受众。相比于一个简单的智能手机手电筒应用程序,对一款商业搜索引擎往往会执行更加严格的测试方法。然而,无论是什么应用,多少测试才足够的问题很难用明确的术语来回答。更好的方法是提供「可用于定义最适合我们手头案例的质量认证过程和测试策略」的「考虑因素或经验法则」。以下指引提供了一个有用的标准:
如果您已经在测试您的产品,请记录整个过程。这对于能够为以后的版本重复测试并对其进行分析以进行进一步改进至关重要。如果这是您的第一个版本,最好有一个书面的测试计划或策略。事实上,任何产品设计都应该有书面的测试计划或策略
一个很好的起点是在编写代码的同时也要伴随编写测试代码。所谓「单元测试」是指测试在功能单元级别编写的代码。通过 mock
或者 fake
来处理对外部服务的依赖项(译者注:作者想表达的是单元测试应该尽可能早写 —— 趁代码还是功能单元级别时)
mock
具有与真实生产环境依赖项相同的接口,但仅检查对象是否按照预期使用或返回,而不关心具体的实现。
另一方面,fake
是依赖项的浅层实现,但理想情况下应该没有它自己的依赖项。Fakes
提供了比 Mocks
更广泛的功能,并且应该由提供依赖项的生产版本的团队维护。这样,随着依赖项的迭代,单元测试编写者可以确信 fake
可以真实反映生产环境依赖项的功能。
在包括 Google 在内的许多公司中,都有要求任何代码更改都要使相应的单元测试用例通过的最佳实践。随着代码库的扩展,在提交代码之前执行大量此类测试是在错误潜入代码库之前捕获错误的重要部分。这可以节省以后编写集成测试、调试和验证对现有代码的修复的时间。
随着代码库的增长并达到可以作为一个组进行测试的功能单元数量的地步,是时候建立一个坚实的集成测试基础了。集成测试需要一小部分单元,通常只有两个单元(译者注:为什么是两个?原文没有提及),并作为一个整体测试它们的行为,验证它们是否可以连贯地协同工作。
开发人员通常认为,相比于完整的端到端测试,集成测试不是高优先级的甚至可以被跳过。毕竟,前者按照用户对产品的真实体验进行了测试。然而,拥有一套全面的集成测试与拥有坚实的单元测试基础同样重要。(请参阅较早的 Google 博客文章:《Fixing a test hourglass》)
原因在于集成测试比完整的端到端测试具有更少的依赖性。因此,只有较小环境依赖的集成测试将比具有全套依赖关系的完整端到端测试更快、更可靠(请参阅较早的 Google 博客文章: 《Test Flakiness - One of the Main Challenges of Automated Testing》)
到目前为止的讨论涵盖了对产品的组件级别测试(单元测试),然后是对一组组件和依赖项的测试(集成测试)。现在是时候像用户真实使用产品一样执行端到端的测试了。这一点非常重要,因为不仅要测试独立的功能,还要测试包含各种功能的整个工作流程。在谷歌,这些操作流程——用户为实现关键目标而执行的一系列操作——被称为用户关键行为 (CUJs)。了解 CUJs,记录它们,然后使用端到端测试(最好用自动化方式)验证它们,完成测试金字塔。
单元测试、集成测试和端到端测试解决了产品的功能级别的问题。了解其他测试层级也很重要,包括:
同时,应该在审查周期中尽早进行这些测试过程。较小的性能测试可以更早地检测出问题,然后尽早进行回归,减少端到端测试期间的调试时间。
到目前为止,已经从质的角度的角度研究了多少测试才足够的问题。我们对不同类型的测试进行了讨论,并提出更小的测试和更早进行测试比越大的测试和越晚进行测试更好的论点。现在我们从量的角度来研究这个问题,同时考虑代码覆盖技术。
Wikipedia 有一篇关于代码覆盖率的好文章,概述并讨论了不同类型的覆盖率,包括语句、边缘、分支和条件覆盖率。有几种开源工具可用于测量大多数流行编程语言(如 Java、C++、Go 和 Python)的覆盖率。下表展示了部分工具:
语言 | 工具 |
---|---|
Java | JaCoCo |
Java | JCov |
Java | OpenClover |
Python | Coverage.py |
C++ | Bullseye |
Go | 内置的覆盖率支持(go -cover) |
这些工具中的大多数都以百分比形式提供覆盖率摘要信息。例如,80% 的代码覆盖率意味着大约 80% 的代码被覆盖,大约 20% 的代码未被覆盖。同时,重要的是要理解,覆盖率仅代表你覆盖了特定的代码区域,并不代表这段代码就没有 bug 了。
覆盖率中有另外一个概念叫增量覆盖率(译者注:区别于全量覆盖率),增量覆盖率代表针对新增/修改代码行的覆盖率情况。对于已经积累了一些技术债务并且在整个代码库中全量覆盖率较低的团队来说,增量覆盖率很有用。这些团队可以制定一项规则,提高他们的增量覆盖率来保证整体覆盖度的改进。
到目前为止,覆盖率的讨论集中在测试(函数、行等)对代码的覆盖情况。另一种类型的覆盖是功能覆盖或行为覆盖。对于功能覆盖,重点是列出特定版本中已提交的功能并为其实现创建测试。对于行为覆盖,重点是列出 用户关键行为(CUJs) 并创建适当的测试来跟踪它们。同样,要明白那些“未覆盖”的特征和行为是衡量风险的有效指标。
改善产品质量认证流程的一个很重要的部分是在产品发布之后收到的及时用户反馈。拥有一个跟踪服务宕机(或者应用程序 crash)、bugs 和其他问题的流程,以改产品质量,对于最大限度地减少后续版本中的回归风险至关重要。此外,还需注意(1)强调在质量认证过程中尽早填补测试空白,(2)解决战略问题,例如缺乏特定类型的测试,例如负载或容错测试。同样,这就是为什么记录质量认证过程很重要,以便可以根据从现场获得的数据进行重新评估。
创建全面的质量认证流程和测试策略来回答“多少测试才足够?”这个问题可能是一项复杂的任务。希望这里给出的指引可以给到一些帮助。总之: