1

考虑以下代码:

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 y其他x and y示例)。

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

长

是否coverage.py支持这样的功能?如果是这样,您如何在您的 cov 报告中启用“内联分支覆盖”?如果没有,是否有任何其他方法可以识别您的测试套件从未实际执行过的“隐藏”代码?

4

2 回答 2

3

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

# 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在 2007 年的一篇文章中coverage.py称这是一个隐藏的条件涵盖了这种情况以及其他coverage.py无法处理的情况。

这个问题也延伸到if语句!举个例子:

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

如果condition_b在为真时始终condition_a为真,则永远不会在 中找到拼写错误condtion_c,如果您仅依赖converage.py,则不会,因为不支持条件覆盖(更不用说更高级的概念,例如修改条件/决策覆盖多条件覆盖)。

支持条件覆盖的一个障碍是技术上的:coverage.py 严重依赖 Python 的内置跟踪支持,但直到最近,这只能让您跟踪每的执行情况。Ned 实际上探索了解决这个问题的方法

并不是说这停止了一个不同的项目,无论如何都有助于提供条件/决策覆盖。该项目使用 AST 重写和导入钩子来添加额外的字节码,让它跟踪单个条件的结果,从而为您提供表达式“真值表”的概述。这种方法有一个巨大的缺点:它非常脆弱,并且经常需要更新新的 Python 版本。结果,该项目与 Python 3.4 中断并没有得到修复。

但是,Python 3.7增加了对操作码级别跟踪的支持,允许跟踪器分析每个单独字节码的效果,而无需求助于 AST 黑客。随着coverage.py 5.0 达到稳定状态,该项目似乎正在考虑增加对条件覆盖的支持,并有可能的赞助商支持开发。

所以你现在的选择是:

  • 在 Python 3.3 中运行您的代码并使用工具
  • 修复工具以在更新的 Python 版本上运行
  • 等待coverage.py添加条件覆盖
  • 帮助编写coverage.py的功能
  • 'opcode'使用 Python 3.7 或更新的跟踪模式将您自己的工具方法组合在一起。
于 2020-02-28T13:35:38.027 回答
-2

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

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

于 2020-02-27T23:36:32.547 回答