Python 使覆盖率只计算成功的测试,而忽略失败的测试

Python 使覆盖率只计算成功的测试,而忽略失败的测试,python,pytest,coverage.py,pytest-cov,Python,Pytest,Coverage.py,Pytest Cov,我有许多项目使用pytest.mark.xfail标记失败但不应该失败的测试,以便在修复问题之前添加失败的测试用例。我不想跳过这些测试,因为如果我做的事情导致他们开始通过测试,我希望得到通知,这样我就可以删除xfail标记以避免回归 问题在于,因为xfail测试实际上一直运行到失败为止,所以导致失败的任何行都被视为“已覆盖”,即使它们是未通过测试的一部分,这就给了我关于有多少代码实际被测试为正常工作的误导性指标。一个最简单的例子是: pkg.py def f(fail): if fail

我有许多项目使用
pytest.mark.xfail
标记失败但不应该失败的测试,以便在修复问题之前添加失败的测试用例。我不想跳过这些测试,因为如果我做的事情导致他们开始通过测试,我希望得到通知,这样我就可以删除
xfail
标记以避免回归

问题在于,因为
xfail
测试实际上一直运行到失败为止,所以导致失败的任何行都被视为“已覆盖”,即使它们是未通过测试的一部分,这就给了我关于有多少代码实际被测试为正常工作的误导性指标。一个最简单的例子是:

pkg.py

def f(fail):
    if fail:
        print("This line should not be covered")
        return "wrong answer"

    return "right answer"
import pytest
from pkg import f

def test_success():
    assert f(fail=False) == "right answer"

@pytest.mark.xfail
def test_failure():
    assert f(fail=True) == "right answer"
test_pkg.py

def f(fail):
    if fail:
        print("This line should not be covered")
        return "wrong answer"

    return "right answer"
import pytest
from pkg import f

def test_success():
    assert f(fail=False) == "right answer"

@pytest.mark.xfail
def test_failure():
    assert f(fail=True) == "right answer"
运行python-m pytest--cov=pkg,我得到:

platform linux -- Python 3.7.1, pytest-3.10.0, py-1.7.0, pluggy-0.8.0
rootdir: /tmp/cov, inifile:
plugins: cov-2.6.0
collected 2 items

tests/test_pkg.py .x                                            [100%]

----------- coverage: platform linux, python 3.7.1-final-0 -----------
Name     Stmts   Miss  Cover
----------------------------
pkg.py       5      0   100%
正如您所看到的,所有五行都被覆盖,但是第3行和第4行仅在
xfail
测试期间被命中

我现在处理这个问题的方法是设置
tox
来运行类似
pytest-m“not xfail”-cov&&pytest-m xfail
,但是除了有点麻烦之外,这只是用
xfail
标记过滤掉一些东西,这意味着条件xfail也会被过滤掉,无论是否满足条件


有没有办法让
coverage
pytest
不计算失败测试的覆盖率?或者,我也可以使用一种忽略
xfail
测试覆盖率的机制,该机制仅在满足条件的情况下忽略条件
xfail
测试。

因为您使用的是
pytest-cov
插件,请利用它的标记。当使用
pytest.mark.no_cover
注释时,测试的代码覆盖率将关闭。唯一需要实现的就是将
no_cover
标记应用于所有标记为
pytest.mark.xfail
的测试。在您的
conftest.py
中:

import pytest

def pytest_collection_modifyitems(items):
    for item in items:
        if item.get_closest_marker('xfail'):
            item.add_marker(pytest.mark.no_cover)
现在运行您的示例将产生:

$ pytest --cov=pkg -v
=================================== test session starts ===================================
platform darwin -- Python 3.7.1, pytest-3.9.1, py-1.7.0, pluggy-0.8.0
cachedir: .pytest_cache
rootdir: /Users/hoefling/projects/private/stackoverflow, inifile:
plugins: cov-2.6.0
collected 2 items

test_pkg.py::test_success PASSED                                                     [ 50%]
test_pkg.py::test_failure xfail                                                      [100%]

---------- coverage: platform darwin, python 3.7.1-final-0 -----------
Name     Stmts   Miss  Cover
----------------------------
pkg.py       5      2    60%


=========================== 1 passed, 1 xfailed in 0.04 seconds ===========================
编辑:处理
xfail
标记中的条件 标记参数可以通过
marker.args
marker.kwargs
访问,因此如果您有一个标记

@pytest.mark.xfail(sys.platform == 'win32', reason='This fails on Windows')
使用访问参数

marker = item.get_closest_marker('xfail')
condition = marker.args[0]
reason = marker.kwargs['reason']

考虑条件标志,上面的钩子可以修改如下:

def pytest_collection_modifyitems(items):
    for item in items:
        marker = item.get_closest_marker('xfail')
        if marker and (not marker.args or marker.args[0]):
            item.add_marker(pytest.mark.no_cover)

这似乎是解决方案的一半;仅当满足故障条件时,有条件的X故障测试还需要关闭覆盖率。不过,我认为这可能解决了问题中最难的部分。哦,您希望对带有
xfail
结果的测试关闭覆盖率(例如,对于带有
xpass
结果的测试关闭覆盖率)?测试完成后覆盖率结果的后处理方式?不,
xfail
采用布尔值,因此可以表示“这在Windows上失败”。我总是使用默认为strict的
xfail
运行,但根据我的经验,无论bool是否为true,测试都会得到
xfail
标记;条件标志是标记参数中的第一个属性,因此不难将其考虑在内。我用一个例子更新了答案。这是一个非常有趣的想法!在coverage 5.0 alpha中,我们可以跟踪哪些测试覆盖了哪些行。如果我们使用pytest插件来帮助实现这一点,也许它可以禁用xfail测试的测量功能。您介意将此作为一个问题写在coverage.py repo上吗@内德:我会的。