原文:Code Coverage Best Practices
作者:Carlos Arguelles, Marko Ivanković‎, and Adam Bender

我们花费了数十年的时间在许多大型软件公司推动软件测试活动。我们一贯倡导的领域之一:使用代码覆盖率数据来评估风险和找出测试中的差距。然而,代码覆盖的价值是一个争论激烈的话题,众说纷纭,而且是一个出人意料的两极分化的话题。每当在任何一个大型群体中提到代码覆盖率时,似乎无休止的争论就会接踵而来。人们总是各自为阵,并不会为争论带来任何有成效的进展。本文档的目的是为你提供工具,引导不同阵营的人找到共同点,以便你能够向前迈进,务实地使用覆盖信息。我们提出了代码覆盖领域的最佳实践,以有效地处理代码健康问题。

  • 代码覆盖率为开发工作流程带来了显而易见的收益。它不是测试质量的最佳衡量标准,但是提供了具有可行数据的合理、客观、行业标准的度量标准。它不需要大量的人为干预,普遍适用于所有产品,并且行业中对于大多数语言都有足够的工具可用。你必须理解这是一个有损的间接指标,它将大量信息压缩成一个数字,所以它不应该是你唯一的真相来源。相反,将其与其他技术结合使用,对你的测试工作进行更全面的评估。
  • 是否仅通过代码覆盖就能减少缺陷是一个开放的研究问题,但是我们的经验表明,增加代码覆盖率的努力通常会导致卓越工程文化的改变,从长远来看会减少缺陷。例如,优先考虑代码覆盖率的团队倾向于将测试活动视为第一优先级,并且倾向于在产品设计过程中加入更强的可测试性,以便他们可以更轻松地实现测试目标。这又反向促使他们开始编写更高质量的代码(API中更具模块化,更简洁的协议,更易于管理的代码审查等)。他们还开始更加关注代码的整体健康状况,工程和运营的卓越性。
  • 高代码覆盖率并不能保证测试覆盖的高质量。把注意力集中在尽可能使得覆盖率接近100%上,会导致错误的安全感。这也可能是一种浪费,浪费机器运行时间,因需要维护的低价值测试活动而产生技术债务。由于缺少测试而被推到生产环境中的错误代码可能是因为:(1)你的测试没有覆盖特定的代码路径,这种测试缺陷很容易通过代码覆盖率分析识别出来;或者(2)你的测试没有覆盖某个代码覆盖率区域中的特定边界情况,这是很难或者不可能被捕捉到的代码覆盖率分析。代码覆盖率并不保证被覆盖的行或分支已被正确测试,它只是保证它们已被测试执行。要注意仅仅为了增加覆盖率而复制/粘贴测试,或者为了达到某个数字而增加没有什么实际价值的测试。有一种更好的技术,评估是否充分运用了你的测试所覆盖的路径,并对失败做出了充分的断言,那就是变异测试
  • 但是,低代码覆盖率的确可以说明在每次部署时都不会通过自动化来完全测试产品的大部分场景。这增加了将错误代码推送到生产环境的风险,因此应引起注意。实际上,代码覆盖率数据的许多价值不是要突出显示所涵盖的内容,而是要突出显示未涵盖的内容。
  • 没有一个可以普遍适用于所有产品的“理想的代码覆盖率”。对于一组代码,你想要/需要的测试级别应该是:(1)代码的业务影响/关键性;(2)你需要接触/更改代码的频率;(3)你希望代码的生命周期、其复杂性和域变量。我们不能要求每一个团队都有n%的代码覆盖率;这是一个最好由拥有特定领域知识的产品所有者做出的商业决策。任何要达到n%代码覆盖率的任务都应该伴随着基础设施的投资以简化测试,比如将工具集成到开发人员的工作流程中。注意,工程师可能会开始将你的目标视为一个复选框,并避免将覆盖范围扩大到目标之外,即使这样做比较谨慎。
  • 一般来说,很多产品的代码覆盖率都低于标准,我们应该以大幅提高全面的代码覆盖率为目标。虽然没有 “理想的代码覆盖率”,但在Google,我们提供的一般准则是60%为 “可接受”,75%为 “值得称赞”,90%为 “模范”。然而我们喜欢远离广泛的自上而下的规定,鼓励每个团队选择对其业务需求有意义的数值。
  • **我们不应该纠结于如何将90%的代码覆盖率提高到95%**。代码覆盖率超过某个点后,覆盖率增加的投入产出比是呈对数级别的(译者注:超过某个覆盖率后投入越多产生的收益越少)。但我们应该采取具体措施,从30%到70%,并始终确保新代码符合我们期望的阈值。
  • 比覆盖行数百分比更重要的是,人们对未被覆盖的实际代码行数(和行为)的判断(分析测试的差距),以及这种风险能否接受。未被覆盖的代码比已覆盖的代码更有意义。在代码审查过程中,对未被覆盖的具体代码行进行务实的讨论,比在一个任意的目标数上进行过度纠结更有价值。我们发现,将代码覆盖率嵌入到你的代码审查过程中,会使代码审查更快更容易。并非所有的代码都同样重要,例如测试调试日志行往往没有那么重要,因此当开发人员不仅可以看到覆盖率,而且可以看到每个被覆盖的行突出显示为代码审查的一部分时,他们可以确保最重要的代码已被覆盖。
  • 仅仅因为你的产品代码覆盖率低,并不意味着你不能采取具体的、渐进的步骤来改进它。继承一个测试效果差,可测试性也差的遗留系统可能会让人望而生畏,你可能感觉不到有能力扭转这个结果,甚至不知道从哪里开始。但至少,你可以采用“童子军规则”(boy-scout rule 把露营地收拾得比你发现它的时候还要干净)。随着时间的推移,你会逐渐到达一个健康的目标。
  • 确保覆盖经常变更的代码。虽然项目范围的目标超过90%的很可能不值得,但每次提交的代码的覆盖率达到99%是合理的,而90%是一个较低的下限。我们需要确保我们的测试不会随着时间的推移而变得越来越差。
  • 单元测试代码的覆盖率只是其中一部分。集成/系统测试代码的覆盖范围也很重要。流水线(原文:Pipeline)中所有来源(单元和集成)的覆盖范围的总体视图至关重要,它为你提供一个更加全面的视图:即你的代码有多少未通过自动化执行到而最终到达了生产环境。你应该意识到的一件事是,虽然单元测试在执行的代码和评估的代码之间具有高度的相关性,但是集成测试和端到端测试中的某些覆盖是偶然的,而不是有意为之的。但是,将集成测试中的代码覆盖范围合并到一起后,可以帮你避免出现一种错误的安全感:即使你没有在单元测试中覆盖代码,但你仍认为自己在集成测试中已经覆盖了代码。
  • 我们应该限制不符合代码覆盖标准的部署。团队应该讨论并确定哪种准入机制对他们有意义。不过,应该小心,不要将其视为必选项,因为它会适得其反(“达到指标”的压力几乎不会产生预期的结果)。有许多可用的机制:全量代码的覆盖率把关 VS 增量代码的覆盖率把关;特定硬编码代码覆盖率把关 VS 以前版本中的增量代码覆盖率把关,要忽略或关注的代码的特定部分。然后,致力于以团队的形式坚持这些原则。违反准入标准的代码覆盖率的下降,应阻止其被检入并进入生产。

如果你想了解有关Google覆盖范围基础架构的更多信息,欢迎你阅读我们的论文“ Coverage at Google”。