Как повторно запускать неудачные тесты и сохранять состояние между запусками тестов¶
Использование¶
Плагин предоставляет две опции командной строки для повторного запуска сбоев с последнего вызова pytest:
--lf,--last-failed- повторное выполнение только сбоев.--ff,--failed-first- для запуска сначала сбоев, а затем остальных тестов.
Для очистки (обычно это не требуется) опция --cache-clear позволяет удалить все содержимое межсессионного кэша перед запуском теста.
Другие плагины могут обращаться к объекту config.cache для установки/получения json-кодируемых значений между вызовами pytest.
Примечание
Этот плагин включен по умолчанию, но при необходимости может быть отключен: см. Деактивация / снятие с регистрации плагина по имени (внутреннее название этого плагина cacheprovider).
Повторное выполнение только отказов или сначала отказов¶
Сначала создадим 50 тестовых вызовов, из которых только 2 окажутся неудачными:
# content of test_50.py
import pytest
@pytest.mark.parametrize("i", range(50))
def test_num(i):
if i in (17, 25):
pytest.fail("bad luck")
Если вы запустите его в первый раз, вы увидите два сбоя:
$ pytest -q
.................F.......F........................ [100%]
================================= FAILURES =================================
_______________________________ test_num[17] _______________________________
i = 17
@pytest.mark.parametrize("i", range(50))
def test_num(i):
if i in (17, 25):
> pytest.fail("bad luck")
E Failed: bad luck
test_50.py:7: Failed
_______________________________ test_num[25] _______________________________
i = 25
@pytest.mark.parametrize("i", range(50))
def test_num(i):
if i in (17, 25):
> pytest.fail("bad luck")
E Failed: bad luck
test_50.py:7: Failed
========================= short test summary info ==========================
FAILED test_50.py::test_num[17] - Failed: bad luck
FAILED test_50.py::test_num[25] - Failed: bad luck
2 failed, 48 passed in 0.12s
Если вы затем запустите его с --lf:
$ pytest --lf
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 2 items
run-last-failure: rerun previous 2 failures
test_50.py FF [100%]
================================= FAILURES =================================
_______________________________ test_num[17] _______________________________
i = 17
@pytest.mark.parametrize("i", range(50))
def test_num(i):
if i in (17, 25):
> pytest.fail("bad luck")
E Failed: bad luck
test_50.py:7: Failed
_______________________________ test_num[25] _______________________________
i = 25
@pytest.mark.parametrize("i", range(50))
def test_num(i):
if i in (17, 25):
> pytest.fail("bad luck")
E Failed: bad luck
test_50.py:7: Failed
========================= short test summary info ==========================
FAILED test_50.py::test_num[17] - Failed: bad luck
FAILED test_50.py::test_num[25] - Failed: bad luck
============================ 2 failed in 0.12s =============================
Вы запустили только два неудачных теста из последнего запуска, а 48 проходящих тестов не были запущены («отменены»).
Теперь, если вы запустите с опцией --ff, будут выполнены все тесты, но сначала будет выполнен первый предыдущий сбой (как видно из серии FF и точек):
$ pytest --ff
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 50 items
run-last-failure: rerun previous 2 failures first
test_50.py FF................................................ [100%]
================================= FAILURES =================================
_______________________________ test_num[17] _______________________________
i = 17
@pytest.mark.parametrize("i", range(50))
def test_num(i):
if i in (17, 25):
> pytest.fail("bad luck")
E Failed: bad luck
test_50.py:7: Failed
_______________________________ test_num[25] _______________________________
i = 25
@pytest.mark.parametrize("i", range(50))
def test_num(i):
if i in (17, 25):
> pytest.fail("bad luck")
E Failed: bad luck
test_50.py:7: Failed
========================= short test summary info ==========================
FAILED test_50.py::test_num[17] - Failed: bad luck
FAILED test_50.py::test_num[25] - Failed: bad luck
======================= 2 failed, 48 passed in 0.12s =======================
Опции New --nf, --new-first: запускать сначала новые тесты, а затем остальные, в обоих случаях тесты также сортируются по времени изменения файла, причем первыми идут более свежие файлы.
Поведение при отсутствии неудачных тестов в последнем запуске¶
Если при последнем запуске ни один тест не прошел, или не были найдены кэшированные данные lastfailed, pytest можно настроить либо на запуск всех тестов, либо ни одного теста, используя опцию --last-failed-no-failures, которая принимает одно из следующих значений:
pytest --last-failed --last-failed-no-failures all # run all tests (default behavior)
pytest --last-failed --last-failed-no-failures none # run no tests and exit
Новый объект config.cache¶
Плагины или код поддержки conftest.py могут получить кэшированное значение, используя объект pytest config. Вот базовый пример плагина, реализующего fixture, который повторно использует ранее созданное состояние во всех вызовах pytest:
# content of test_caching.py
import pytest
def expensive_computation():
print("running expensive computation...")
@pytest.fixture
def mydata(request):
val = request.config.cache.get("example/value", None)
if val is None:
expensive_computation()
val = 42
request.config.cache.set("example/value", val)
return val
def test_function(mydata):
assert mydata == 23
Если вы выполняете эту команду в первый раз, вы можете увидеть оператор печати:
$ pytest -q
F [100%]
================================= FAILURES =================================
______________________________ test_function _______________________________
mydata = 42
def test_function(mydata):
> assert mydata == 23
E assert 42 == 23
test_caching.py:19: AssertionError
-------------------------- Captured stdout setup ---------------------------
running expensive computation...
========================= short test summary info ==========================
FAILED test_caching.py::test_function - assert 42 == 23
1 failed in 0.12s
Если вы запустите его во второй раз, значение будет получено из кэша и ничего не будет напечатано:
$ pytest -q
F [100%]
================================= FAILURES =================================
______________________________ test_function _______________________________
mydata = 42
def test_function(mydata):
> assert mydata == 23
E assert 42 == 23
test_caching.py:19: AssertionError
========================= short test summary info ==========================
FAILED test_caching.py::test_function - assert 42 == 23
1 failed in 0.12s
Более подробную информацию см. в config.cache fixture.
Проверка содержимого кэша¶
Вы всегда можете посмотреть содержимое кэша, используя опцию командной строки --cache-show:
$ pytest --cache-show
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
cachedir: /home/sweet/project/.pytest_cache
--------------------------- cache values for '*' ---------------------------
cache/lastfailed contains:
{'test_caching.py::test_function': True}
cache/nodeids contains:
['test_caching.py::test_function']
cache/stepwise contains:
[]
example/value contains:
42
========================== no tests ran in 0.12s ===========================
--cache-show принимает необязательный аргумент для указания шаблона glob для фильтрации:
$ pytest --cache-show example/*
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
cachedir: /home/sweet/project/.pytest_cache
----------------------- cache values for 'example/*' -----------------------
example/value contains:
42
========================== no tests ran in 0.12s ===========================
Очистка содержимого кэша¶
Вы можете дать команду pytest очистить все файлы и значения кэша, добавив опцию --cache-clear следующим образом:
pytest --cache-clear
Это рекомендуется для вызовов с серверов непрерывной интеграции, где изоляция и корректность важнее скорости.
Пошаговая¶
В качестве альтернативы --lf -x, особенно для случаев, когда вы ожидаете, что большая часть набора тестов потерпит неудачу, --sw, --stepwise позволяет вам исправлять их по одному за раз. Набор тестов будет выполняться до первого сбоя, а затем остановится. При следующем вызове тесты продолжатся с последнего неудачного теста и будут выполняться до следующего неудачного теста. Вы можете использовать опцию --stepwise-skip, чтобы проигнорировать один сбойный тест и остановить выполнение теста на втором сбойном тесте. Это полезно, если вы застряли на неудачном тесте и хотите просто проигнорировать его до более позднего времени. Предоставление опции --stepwise-skip также неявно включит опцию --stepwise.