> 文档中心 > 检查了600+个代码库,竟有3%代码未能通过单元测试

检查了600+个代码库,竟有3%代码未能通过单元测试

编者按:有一种特殊类型的单元测试,这种方法可以通过单元测试,但结果并非是我们想要的。本文作者将其命名为薛定谔的单元测试。在这篇文章中,作者在使用666个Python代码库,以检测薛定谔的单元测试,会有什么样的结果,我们不妨一探究竟。

原文链接:https://richardtier.com/2022/02/16/3-of-666-python-codebases-we-checked-had-silently-failing-unit-tests-and-we-fixed-them-all%ef%bf%bc/

译者 | 章雨铭       责编 | 屠敏

我们将一种特殊类型的单元测试命名为:薛定谔的单元测试

这种方法可以通过单元测试,但结果并非是我们想要的。

这个测试不会失败。

这篇文章主要介绍我们对666个Python代码库的代码进行扫描,以检测薛定谔的单元测试。具体来说,我们在20个代码库中发现了一个gotcha(在计算机编程领域中是指在系统或程序、程序设计语言中,合法有效,但是会误解意思的构造,程式容易造成错误,或是一些易于使用但其结果不如期望的构造)之后,就会推送一个自动生成的修复程序:

你发现错误了吗?对于开发者来说,这是一个较为常见的错误:将assertEqual 和assertTrue混淆了。事实上,在我们检查的666个代码库中,我们发现有3%的代码混淆了这两者。这是一个真实存在的问题,会对真实的代码库产生影响,甚至是对那些拥有强大社区和代码审查操作的大型活跃代码库。

譬如:详细名单见(https://gist.github.com/code-review-doctor/9b0b4be2129bc2d4678f4e205aec8811)

  • Python.org (很讽刺)

  • Celery

  • UK Ministry of Justice

  • Ansible

  • Django CMS

  • Keras

  • PyTorch

  • Ray

  • Salt

这个问题如此普遍,这表明在代码审查中很容易忽略该问题,是导致这个问题的关键原因。快速浏览代码时,这些错误不会引人注意。并且看起来可行。

薛定谔单元测试

19世纪末,马克吐温曾说:“使我们陷入困境的不是无知,而是看似正确的谬误论断。”19世纪文学中的道理,在21世纪早期Python单元测试中仍然适用:测试不想测试的内容不如不测试。因为薛定谔的测试盲目地相信这个功能是可行的。

你进行过下面的工作流程吗:

  • 提交被混淆后的代码,在本地通过功能测试。

  • 拉取请求生成时,测试在CI中运行,未报告任何失败。

  • 在代码审核阶段,同行审核并通过了该变更。“LGTM!”(代码已经审核过,可以合并)

  • 运用被混淆的代码,但在初次遇到用户时就会失败。

  • 阅读完错误报告后,还自言自语道:“但测试通过了!”。

在重新审查单元测试时,我们注意到,在软件开发生命周期中如此依赖的单元测试实际上根本没有测试功能。如果你遇到了这个问题,那么你会遇到这类的单元测试。TestCase.assertTrue就是其中之一。

TestCase.assertTure gotcha

虽然pytest非常流行,但28%的代码库仍然使用内置的unittest包。

使用内置unittest包的代码库存在assertTrue gotcha的风险。

assertTrue的文档表明,测试expr结果为Ture,但这个解释不完整。

实际上,测试expr结果为Ture,这意味着如果将以下任何值用作第一个参数,测试将通过:

'foo', ‘bar’, 1, [1], {‘a’}, True

反之,如果值错误,测试将失败:

'', 0, [], {}, False,

assertTrue还接受第二个参数,这是显示第一个参数是否正确的自定义错误消息。

这个调用签名通过这个错误,测试通过,因此可能会无声地失败。

例如,在制作20个PR修复问题时,我们发现,一旦测试不再是薛定谔单元测试,其中一个测试由于应用程序逻辑错误而失败:

我们还发现,因为测试中的逻辑错误,至少有两个测试失败。如果我们把单元测试看作是描述产品如何运作的一种文档形式,那也很糟糕。

从开发人员的角度来看,单元测试比注释和文档字符串更可信,因为注释和文档字符串是编写的声明(可能很久以前,也可能现在已经过时或不完整),而通过的单元测试表明逻辑是可行的……只要测试不是薛定谔测试!

也许我们需要更多的数据,但这可能意味着薛定谔的单元测试中有15%(20分之3)隐藏了损坏的功能。

TestCase.asserEqual

当代码审查时,CodeReview.doctor github bot在GitHub pull请求中检测到此错误,建议使用以下解决方案: