尽管有 100% 的覆盖率报告,如何找到从未在 coverage.py 中执行的代码

How to find code which was never executed in coverage.py despite a 100% coverage report

考虑以下代码:

import math

def dumb_sqrt(x):
    result = math.sqrt(x) if x >= 0 else math.sqrt(-x)*j
    return result


def test_dumb_sqrt():
    assert dumb_sqrt(9.) == 3.

测试可以这样执行:

$ pip install pytest pytest-cov
$ pytest test_thing.py --cov=test_thing --cov-report=html --cov-branch

覆盖率报告将考虑 100% 覆盖的所有行,即使启用了分支覆盖率也是如此:

但是,这段代码有一个错误,眼尖的人可能已经看到了。如果它进入 "else" 分支,将会出现异常:

NameError: global name 'j' is not defined

修复错误很容易:将未定义的 j 名称更改为文字 1j。添加另一个可以揭示错误的测试也很容易:assert dumb_sqrt(-9.) == 3j。这个问题也不是在问什么。我想知道如何找到从未实际执行过的代码部分,尽管有 100% 的代码覆盖率报告。

使用条件表达式就是这样的罪魁祸首,但在任何地方都有类似的情况 Python 可以使评估短路(x or yx and y 是其他示例)。

最好是,上面的第 4 行在报告中可以显示为黄色,类似于 "if" 行如果首先避免使用条件表达式时的呈现方式:

coverage.py支持这样的功能吗?如果是这样,您如何在您的 cov 报告中启用 "inline branch coverage"?如果不是,是否有任何其他方法可以识别 "hidden" 从未由您的测试套件实际执行的代码?

不要使用 res1 if cond else res2 - 它必须被视为单个语句。如果你把它写成 if/else,我认为 coverage.py 会做得更好。

并考虑使用 pylint 之类的东西,或者至少使用 pyflakes。我相信这些会自动检测到问题。

不,coverage.py 不处理表达式中的条件分支。这不仅会影响 Python 条件表达式,使用 andor 也会受到影响:

# pretending, for the sake of illustration, that x will never be 0
result = x >= 0 and math.sqrt(x) or math.sqrt(-x)*j

Ned Batchelder,coverage.py 的维护者,在 article from 2007 covering this and other cases coverage.py can't handle.

中称其为 隐藏条件

此问题也扩展到 if 语句!举个例子:

if condition_a and (condition_b or condtion_c):
    do_foo()
else:
    do_bar()

如果 condition_bcondition_a 为真时始终为真,那么您将永远找不到 condtion_c 中的拼写错误,如果您仅依赖 converage.py,则不会,因为不支持条件覆盖(更不用说像 modified condition/decision coverage and multiple condition coverage.

这样更高级的概念了

支持条件覆盖的一个障碍是技术:coverage.py relies heavily on Python's built-in tracing support, but until recently this would only let you track execution per line. Ned actually explored work-arounds for this issue.

并不是说那阻止了另一个项目,instrumental 提供 condition/decision 覆盖率 无论如何 。该项目使用 AST 重写和导入挂钩来添加额外的字节码,使其能够跟踪各个条件的结果,从而为您提供 'truth table' 表达式的概览。这种方法有一个巨大的缺点:它非常脆弱,并且经常需要为新的 Python 版本进行更新。结果,该项目在 Python 3.4 中中断并且尚未修复。

然而,Python 3.7 added support for tracing at the opcode level, allowing a tracer to analyse the effect of each individual bytecode without having to resort to AST hacking. And with coverage.py 5.0 having reached a stable state, it appears that the project is considering adding support for condition coverage,可能有赞助商支持开发。

所以你现在的选择是:

  • 运行 您在 Python 3.3 中的代码并使用 instrumental
  • 在更新的 Python 版本上修复对 运行 的帮助
  • 等待coverage.py添加条件覆盖
  • 帮助编写 coverage.py
  • 的功能
  • 使用 Python 3.7 或更新的 'opcode' 跟踪模式将您自己的工具方法组合在一起。