不稳定的测试

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

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

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

潜在的根本原因

系统状态

广义地说,不稳定的测试表明测试依赖于未被适当控制的某些系统状态——测试环境没有充分隔离。高级别的测试更容易不稳定,因为它们依赖更多的状态。

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

  • 也许不同的测试在运行后未能清理自身,留下了导致不稳定测试失败的数据。

  • 不稳定的测试依赖于先前测试的数据,而该先前测试在运行后未能清理自身,并且在并行运行中,该先前测试并非总是存在。

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

过于严格的断言

过于严格的断言可能导致浮点比较问题以及时间问题。pytest.approx() 在这里很有用。

线程安全

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

即使是并行运行测试的插件,例如 pytest-xdist,通常也是通过生成多个 进程 并分批运行测试来工作,而不是使用多线程。

当然,测试和 fixtures 可以在其测试工作流程中自行生成线程(例如,在后台启动服务器线程的 fixture,或执行生成线程的生产代码的测试),这是可能且常见的,但必须小心。

  • 确保最终等待任何生成的线程——例如在测试结束时,或在 fixture 的拆卸期间。

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

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

其他通用策略

拆分测试套件

将单个测试套件拆分为两个,例如单元测试和集成测试,并仅将单元测试套件用作 CI 门禁,这可能很常见。这也有助于控制构建时间,因为高级别测试往往较慢。然而,这意味着破坏构建的代码有可能被合并,因此需要对集成测试结果进行额外警惕。

失败时录像/截图

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

删除或重写测试

如果功能已由其他测试覆盖,也许可以删除该测试。如果不是,也许可以将其重写到更低的层面,这将消除不稳定性或使其来源更明显。

隔离

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

失败时重试的 CI 工具

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

研究

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

  • Gao, Zebao, Yalan Liang, Myra B. Cohen, Atif M. Memon 和 Zhen Wang。“使系统用户交互测试可重复:我们何时以及应该控制什么?”。载于 Software Engineering (ICSE), 2015 IEEE/ACM 37th IEEE International Conference on, 第 1 卷, 第 55-65 页。IEEE, 2015。PDF

  • Palomba, Fabio 和 Andy Zaidman。“测试异味重构是否能修复不稳定测试?”。载于 Software Maintenance and Evolution (ICSME), 2017 IEEE International Conference on, 第 1-12 页。IEEE, 2017。Google Drive 中的 PDF

  • Bell, Jonathan, Owolabi Legunsen, Michael Hilton, Lamyaa Eloussi, Tifany Yung 和 Darko Marinov。“DeFlaker:自动检测不稳定测试。”。载于 Proceedings of the 2018 International Conference on Software Engineering。2018。PDF

  • Dutta, Saikat 和 Shi, August 和 Choudhary, Rutvik 和 Zhang, Zhekun 和 Jain, Aryaman 和 Misailovic, Sasa。“在概率和机器学习应用中检测不稳定测试。”。载于 Proceedings of the 29th ACM SIGSOFT International Symposium on Software Testing and Analysis (ISSTA), 第 211-224 页。ACM, 2020。PDF

  • Habchi, Sarra 和 Haben, Guillaume 和 Sohn, Jeongju 和 Franci, Adriano 和 Papadakis, Mike 和 Cordy, Maxime 和 Le Traon, Yves。“是什么导致了这个测试不稳定?查明导致测试不稳定的类。”。载于 Proceedings of the 38th IEEE International Conference on Software Maintenance and Evolution (ICSME), IEEE, 2022。PDF

  • Lamprou, Sokrates。“非确定性测试及其发现:通过检查测试顺序依赖性,实证研究不稳定测试与测试异味之间的关系。”。学士论文,计算机与信息科学系,林雪平大学,2022。LIU-IDA/LITH-EX-G–19/056–SE。PDF

  • Leinen, Fabian 和 Elsner, Daniel 和 Pretschner, Alexander 和 Stahlbauer, Andreas 和 Sailer, Michael 和 Jürgens, Elmar。“持续集成中不稳定测试的成本:一项工业案例研究。”。慕尼黑工业大学和 CQSE 有限公司,慕尼黑,德国,2023。PDF

资源