Используя параметризацию pytest, как я могу пропустить оставшиеся тесты, если один тест не пройден? - PullRequest
0 голосов
/ 12 сентября 2018

Я использую pytest.mark.parametrize для подачи все более длинных входов в довольно медленную тестовую функцию, например так:

@pytest.mark.parametrize('data', [
    b'ab',
    b'xyz'*1000,
    b'12345'*1024**2,
    ... # etc
])
def test_compression(data):
    ... # compress the data
    ... # decompress the data
    assert decompressed_data == data

Поскольку сжатие больших объемов данных занимает много времениЯ хотел бы пропустить все оставшиеся тесты после одного из них.Например, если тест не пройден с входными данными b'ab' (первый), b'xyz'*1000 и b'12345'*1024**2, а все остальные параметризации должны быть пропущены (или xfail без выполнения).

Я знаю, что это возможночтобы прикрепить метки к отдельным параметризациям следующим образом:

@pytest.mark.parametrize("test_input,expected", [
    ("3+5", 8),
    ("2+4", 6),
    pytest.param("6*9", 42, marks=pytest.mark.xfail),
])

Но я не знаю, как можно условно применить эти метки в зависимости от состояния предыдущего контрольного примера.Есть ли способ сделать это?

Ответы [ 2 ]

0 голосов
/ 12 сентября 2018

Оценки оцениваются до выполнения тестов, поэтому невозможно пройти какой-либо декларативный знак (например, skipif), который зависит от результатов других тестов. Однако вы можете применить собственную логику пропуска тестов в хуках. Изменение инкрементного тестирования - шаги теста рецепт из pytest документов:

# conftest.py
import pytest

def pytest_sessionstart(session):
    session.failednames = set()

def pytest_runtest_makereport(item, call):
    if call.excinfo is not None:
        item.session.failednames.add(item.originalname)

def pytest_runtest_setup(item):
    if item.originalname in item.session.failednames:
        pytest.skip("previous test failed (%s)" % item.name)  # or use pytest.xfail like in the other answer

Пример теста

@pytest.mark.parametrize('i', range(10))
def test_spam(i):
    assert i != 3

выходы:

=================================== test session starts ===================================
collected 10 items

test_spam.py::test_spam[0] PASSED
test_spam.py::test_spam[1] PASSED
test_spam.py::test_spam[2] PASSED
test_spam.py::test_spam[3] FAILED
test_spam.py::test_spam[4] SKIPPED
test_spam.py::test_spam[5] SKIPPED
test_spam.py::test_spam[6] SKIPPED
test_spam.py::test_spam[7] SKIPPED
test_spam.py::test_spam[8] SKIPPED
test_spam.py::test_spam[9] SKIPPED

========================================= FAILURES ========================================
_______________________________________ test_spam[3] ______________________________________

i = 3

    @pytest.mark.parametrize('i', range(10))
    def test_spam(i):
>       assert i != 3
E       assert 3 != 3

test_spam.py:5: AssertionError
====================== 1 failed, 3 passed, 6 skipped in 0.06 seconds ======================

Редактировать: работа с пользовательскими маркерами

def pytest_runtest_makereport(item, call):
    markers = {marker.name for marker in item.iter_markers()}
    if call.excinfo is not None and 'skiprest' in markers:
        item.session.failednames.add(item.originalname)

def pytest_runtest_setup(item):
    markers = {marker.name for marker in item.iter_markers()}
    if item.originalname in item.session.failednames and 'skiprest' in markers:
        pytest.skip(item.name)

Использование:

@pytest.mark.skiprest
@pytest.mark.parametrize('somearg', ['a', 'b', 'c'])
def test_marked(somearg):
    ...
0 голосов
/ 12 сентября 2018

РЕДАКТИРОВАТЬ: я приспособил мое решение к принятому нами usint pytest.skip в приспособлении.

Следующий хак может решить вашу проблему. Поддельная тестовая функция test_compression всегда завершается ошибкой, но выполняется только один раз. Но для этого необходимо, чтобы вы добавили проверку прибора alrady_failed в свою тестовую функцию:

import pytest


@pytest.fixture(scope="function")
def skip_if_already_failed(request, failed=set()):
    key = request.node.name.split("[")[0]
    failed_before = request.session.testsfailed
    if key in failed:
        pytest.skip("previous test {} failed".format(key))
    yield
    failed_after = request.session.testsfailed
    if failed_before != failed_after:
        failed.add(key)


@pytest.mark.parametrize("data", [1, 2, 3, 4, 5, 6])
def test_compression(data, skip_if_already_failed):
    assert data < 3

И это вывод:

$ py.test -v sopytest.py
================================== test session starts ==================================
platform darwin -- Python 3.6.6, pytest-3.8.0, py-1.6.0, pluggy-0.7.1 -- ...
cachedir: .pytest_cache
rootdir: ..., inifile:
collected 6 items

sopytest.py::test_compression[1] PASSED                                           [ 16%]
sopytest.py::test_compression[2] PASSED                                           [ 33%]
sopytest.py::test_compression[3] FAILED                                           [ 50%]
sopytest.py::test_compression[4] SKIPPED                                          [ 66%]
sopytest.py::test_compression[5] SKIPPED                                          [ 83%]
sopytest.py::test_compression[6] SKIPPED                                          [100%]

======================================= FAILURES ========================================
__________________________________ test_compression[3] __________________________________

data = 3, skip_if_already_failed = None

    @pytest.mark.parametrize("data", [1, 2, 3, 4, 5, 6])
    def test_compression(data, skip_if_already_failed):
>       assert data < 3
E       assert 3 < 3

sopytest.py:18: AssertionError
===================== 1 failed, 2 passed, 3 skipped in 0.08 seconds =====================
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...