不稳定的测试 (Flaky tests)

“不稳定”(flaky)的测试是指表现出间歇性或偶发性失败、似乎具有非确定性行为的测试。它们有时通过,有时失败,且原因不明。本页面讨论了可以提供帮助的 pytest 功能,以及用于识别、修复或缓解这些问题的其他通用策略。

为什么不稳定的测试是个问题

在使用持续集成(CI)服务器时,不稳定的测试尤其麻烦,因为在合并新的代码更改之前,所有测试都必须通过。如果测试结果不是一个可靠的信号(即测试失败意味着代码更改破坏了测试),开发人员可能会对测试结果失去信任,这可能导致忽略真正的失败。这也是浪费时间的一个来源,因为开发人员必须重新运行测试套件并调查虚假的失败。

潜在的根本原因

系统状态

从广义上讲,不稳定的测试表明测试依赖于某些未得到适当控制的系统状态——测试环境隔离不足。更高级别的测试由于依赖的状态更多,因此更容易变得不稳定。

当并行运行测试套件(例如使用 pytest-xdist)时,有时会出现不稳定的测试。这可能表明测试依赖于测试执行的顺序。

  • 也许是另一个测试在执行后未能清理干净,留下了导致不稳定测试失败的数据。

  • 该不稳定测试依赖于前一个未能清理干净的测试所产生的数据,而在并行运行时,前一个测试并不总是存在。

  • 修改全局状态的测试通常无法并行运行。

过于严格的断言

过于严格的断言会导致浮点数比较问题以及计时问题。pytest.approx() 在这里非常有用。

线程安全性

pytest 是单线程的,始终在同一个线程中顺序执行其测试,自身从不创建任何线程。

即使对于并行运行测试的插件(例如 pytest-xdist),通常也是通过创建多个进程并分批运行测试来实现的,而不使用多线程。

当然,测试和固件(fixtures)作为其测试工作流程的一部分自行创建线程是可能的(也是常见的)(例如,一个在后台启动服务器线程的固件,或一个执行生产代码的测试),但必须格外小心。

  • 确保最终等待所有创建的线程结束——例如在测试结束时,或在固件的销毁(teardown)阶段。

  • 避免在多个线程中使用 pytest 提供的原语(pytest.warns(), pytest.raises() 等),因为它们不是线程安全的。

如果您的测试套件使用线程并且您看到了不稳定的测试结果,不要排除测试在隐式使用 pytest 本身的全局状态的可能性。

其他通用策略

拆分测试套件

将单个测试套件拆分为两个(例如单元测试 vs 集成测试)并仅将单元测试套件用作 CI 门禁是常见的做法。这也有助于保持构建时间在可控范围内,因为高级别测试通常较慢。然而,这意味着确实有可能合入破坏构建的代码,因此需要额外警惕监控集成测试结果。

失败时的视频/截图

对于 UI 测试,这些对于理解测试失败时 UI 的状态非常重要。pytest-splinter 可以与 pytest-bdd 等插件一起使用,并且可以在测试失败时保存截图,这有助于隔离原因。

删除或重写测试

如果其他测试覆盖了该功能,也许可以删除该测试。如果不能,也许可以在更低级别重写它,这将消除不稳定或使其来源更明显。

隔离 (Quarantine)

Mark Lapierre 在 2018 年的一篇文章中讨论了隔离测试的优缺点

失败时重试的 CI 工具

Azure Pipelines(Azure 云 CI/CD 工具,前身为 Visual Studio Team Services 或 VSTS)具有识别不稳定测试并重试失败测试的功能。

研究

这是一个有限的列表,请提交问题或拉取请求来扩展它!

  • Gao, Zebao 等,“Making system user interactive tests repeatable: When and what should we control?.” In Software Engineering (ICSE), 2015 IEEE/ACM 37th IEEE International Conference on, vol. 1, pp. 55-65. IEEE, 2015. PDF

  • Palomba, Fabio, and Andy Zaidman. “Does refactoring of test smells induce fixing flaky tests?.” In Software Maintenance and Evolution (ICSME), 2017 IEEE International Conference on, pp. 1-12. IEEE, 2017. Google Drive 中的 PDF

  • Bell, Jonathan 等,“DeFlaker: Automatically detecting flaky tests.” In Proceedings of the 2018 International Conference on Software Engineering. 2018. PDF

  • Dutta, Saikat 等,“Detecting flaky tests in probabilistic and machine learning applications.” In Proceedings of the 29th ACM SIGSOFT International Symposium on Software Testing and Analysis (ISSTA), pp. 211-224. ACM, 2020. PDF

  • Habchi, Sarra 等,“What Made This Test Flake? Pinpointing Classes Responsible for Test Flakiness.” In Proceedings of the 38th IEEE International Conference on Software Maintenance and Evolution (ICSME), IEEE, 2022. PDF

  • Lamprou, Sokrates. “Non-deterministic tests and where to find them: Empirically investigating the relationship between flaky tests and test smells by examining test order dependency.” 本科论文, 林雪平大学, 2022. LIU-IDA/LITH-EX-G–19/056–SE. PDF

  • Leinen, Fabian 等,“Cost of Flaky Tests in Continuous Integration: An Industrial Case Study.” 慕尼黑工业大学和 CQSE GmbH, 德国慕尼黑, 2023. PDF

资源