e06084's Blog

读《Just Say No to More End-to-End Tests》

Published at 2022-08-21 | Last Update 2022-08-21

前言

书写自动化测试用例是测试开发日常的工作内容。

自动化的技术栈也有难度梯度。

本文是翻译Google2015年的一篇文章《Just Say No to More End-to-End Tests》

借此学习并思考,自动化测试设计中的分层思想。

主要思想

不要迷信e2e测试,也就是e2e测试用例不是多多益善。

为什么呢?

先解释什么是e2e测试以及作者认为理想的测试金字塔结构。

什么是e2e测试?

模拟真实用户场景的测试

理论上e2e测试很好

  • 开发人员喜欢它,因为它将大部分(如果不是全部)测试分担给其他人。
  • 经理和决策者喜欢它,因为模拟真实用户场景的测试可以帮助他们轻松确定失败的测试将如何影响用户。
  • 测试人员喜欢它,因为他们经常担心会遗漏错误或编写无法验证真实行为的测试;从用户的角度编写测试通常可以避免这两个问题,并给测试人员更大的成就感。

实践中e2e测试的问题

作者举了这样一个例子,在Google Docs产品里面,他们是这样运行e2e测试用例的:

  • 构建了最新版本的服务。
  • 然后将该版本部署到团队的测试环境中。
  • 然后,所有端到端测试都针对此测试环境运行。
  • 总结测试结果的电子邮件报告将发送给团队。

为了保持产品质量的高标准,要求e2e用例通过率必须达到90%才能上线。(我:看起来合理安排和要求,应该很多公司也是这么做的)

然后,一次一个新功能还有1天就要发布,然后发生了下面的情况:

剩余天数 通过率 % 备注
1 5% 一切都被打破!登录服务已损坏。几乎所有测试都会登录用户,因此几乎所有测试都失败了。
0 4% 我们依赖的合作伙伴团队昨天在他们的测试环境中部署了一个糟糕的构建。
-1 54% 昨天(或前一天?),开发人员打破了保存场景。一半的测试会在某个时间点保存文档。开发人员一天中的大部分时间都在确定它是前端错误还是后端错误。
-2 54% 这是一个前端错误,开发人员今天花了一半时间弄清楚在哪里。
-3 54% 昨天检查了一个错误的修复程序。不过,这个错误很容易发现,并且今天检查了正确的修复程序。
-4 1% 我们的测试环境在实验室中发生了硬件故障。
-5 84% 隐藏在大错误后面的许多小错误(例如,登录失败、保存失败)。仍在处理小错误。
-6 87% 我们应该在 90% 以上,但由于某种原因不是。
-7 89.54% (四舍五入到 90%,足够接近。)昨天没有检查任何修复,所以昨天的测试一定是不稳定的。

复盘一下整个过程:(我:是不是有一种似曾相似的感觉)

  • 分析&成果:
    • 尽管存在许多问题,但测试最终确实发现了真正的错误。并且在交付客户前修复。
  • 有哪些问题?
    • 该团队晚了一周完成了他们的编码里程碑(并且加班很多)。
    • 找到端到端测试失败的根本原因是痛苦的,并且可能需要很长时间。
    • 合作伙伴的失败和实验室的失败破坏了多天的测试结果。
    • 许多较小的错误隐藏在较大的错误后面。
    • 端到端测试有时很不稳定。
    • 开发人员必须等到第二天才能知道修复是否有效。

(我:看到哪些典型的问题)

  • 1,0,-4 这3天通过率极低问题,归因于“测试环境不稳定”;
  • -1,-2 这3天都是在定位问题,归因于“定位问题不够方便高效”;
  • -3 这一天验证了昨天的修复生效了,归因于“e2e测试需要隔天验证” - 这个不应该,应该容易做到及时验证;
  • -6 这1天通过率差一点,什么都没做,第二天就提高了,归因于“测试用例不稳定,有flaky测试用例”;

针对这个问题,作者引出自己的观点

测试真正的价值

  • 测试真正的价值是解决bug而不是发现bug;(我:咋一看是一回事,深深品一下,两者差异还是比较大的,因为发现bug是不是有效,需要投入多大精力去定位问题都是问题)

建立正确的反馈回路

  • 快。没有开发人员愿意等待数小时或数天才能确定他们的更改是否有效。有时改变不起作用——没有人是完美的——反馈循环需要运行多次。更快的反馈循环导致更快的修复。如果循环足够快,开发人员甚至可以在checking更改之前运行测试。 (我:这个presubmit check是正解,我们已经是这么做的,且效果很好)
  • 可靠。没有开发人员愿意花费数小时调试测试,却发现这是一个不稳定的测试。不稳定的测试降低了开发人员对测试的信任,因此经常忽略不稳定的测试,即使他们发现了真正的产品问题。
  • 隔离了故障。要修复错误,开发人员需要找到导致错误的特定代码行。当一个产品包含数百万行代码,并且漏洞可能在任何地方时,就像大海捞针一样。

想得更小,而不是更大

  • 单元测试 - 作者认为单元测试完美满足上面“正确反馈回路”的要求
    • 单元测试很快。我们只需要构建一个小单元来测试它,而且测试也往往相当小。事实上,对于单元测试来说,十分之一秒被认为是慢的。
    • 单元测试是可靠的。简单的系统和小单元一般来说受到flakiness的影响要小得多。此外,单元测试的最佳实践——特别是与密封测试相关的实践——将完全消除flakiness。 (我:但是日常单元测试的设计也不都是简单的函数级别测试,flakiness的情况也是会存在,但是比e2e的概率应该会小一些)
    • 单元测试隔离故障。即使一个产品包含数百万行代码,如果一个单元测试失败,你只需要搜索那个被测试的小单元就能找到错误。 (我:隔离性是单元测试比较明显的优势,但是从另一个方面来说也是它的局限性)
  • 单元测试和e2e测试
    • 对于端到端测试,您必须等待:首先构建整个产品,然后部署它,最后运行所有端到端测试。当测试运行时,不稳定的测试往往成为现实。即使测试发现了错误,该错误也可能存在于产品中的任何位置。
    • 尽管端到端测试在模拟真实用户场景方面做得更好,但这种优势很快就被端到端反馈循环的所有缺点所抵消。
  • 集成测试
    • 集成测试是scope比单元测试更大一些的测试设计。
    • 为什么需要集成测试?单元测试确实有一个主要缺点:即使这些单元单独运行良好,您也不知道它们是否一起运行良好。但即便如此,您也不一定需要端到端测试。为此,您可以使用集成测试。集成测试需要一小组单元,通常是两个单元,并作为一个整体测试它们的行为,验证它们是否可以连贯地协同工作。
    • 如果两个单元不能正确集成,当您可以编写一个更小、更集中的集成测试来检测相同的错误时,为什么还要编写端到端测试呢?虽然您确实需要考虑更大的范围,但您只需考虑更大的范围即可验证单元是否可以协同工作。
  • 测试金字塔:
    • 上面作者说了很多e2e测试的不足,但是由于它覆盖场景的不可替代性,实际中我们仍然需要少量的端到端测试来验证整个系统。
    • 最后划重点:三种测试类型之间的平衡,作者用下面测试金字塔的可视化表达:

    • 大部分测试是金字塔底部的单元测试。随着您向上移动金字塔,您的测试会变大,但同时测试的数量(金字塔的宽度)会变小。
    • Google 通常建议 70/20/10 拆分:70% 的单元测试、20% 的集成测试和 10% 的端到端测试。每个团队的确切组合会有所不同,但总的来说,它应该保持金字塔形状。

结语

我个人还是比较同意作者的大部分观点。

测试金字塔的分层的抽象思想在很多年前就看过,但是再次去看它还是会有更深的体会。并且,我自己工作中一些测试设计也一直是基于此思想实践的。