Заставьте охват только считать успешные тесты и игнорируйте тесты xfailing

У меня есть ряд проектов, где я использую 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"

test_pkg.py

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 mark, что означает, что условные xfails также отфильтровываются независимо от того, выполняется ли условие.

Есть ли способ иметь coverage или же pytest не рассчитывать покрытие от неудачных тестов? Кроме того, я был бы в порядке с механизмом игнорировать освещение от xfail тесты, которые игнорируют только условные xfail проверяет, выполняется ли условие

1 ответ

Решение

Поскольку вы используете pytest-cov плагин, воспользуйтесь его no_cover маркер. Когда отмечены 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/stackru, 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)
Другие вопросы по тегам