Как повторно запускать неудачные тесты и сохранять состояние между запусками тестов
Использование
Плагин предоставляет два параметра командной строки для повторного запуска ошибок из последнего вызова 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-9.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-9.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 =======================
Новый параметр --nf, --new-first: сначала запускает новые тесты, а затем остальные. В обоих случаях тесты также сортируются по времени изменения файла: сначала идут более новые файлы.
Поведение, если при последнем запуске не было неудачных тестов
Параметр --lfnf, --last-failed-no-failures определяет поведение параметра --last-failed. Он задаёт, следует ли выполнять тесты, если нет известных предыдущих ошибок или не найдены кэшированные данные lastfailed.
Доступны два варианта:
-
all: если нет известных ошибок тестов, запускает все тесты (полный набор тестов). Используется по умолчанию. -
none: если нет известных ошибок тестов, выводит сообщение об этом и завершает работу с успешным кодом.
Пример:
pytest --last-failed --last-failed-no-failures all # runs the full test suite (default behavior) pytest --last-failed --last-failed-no-failures none # runs no tests and exits successfully
Новый объект config.cache
Плагины или вспомогательный код в conftest.py могут получить кэшированное значение с помощью объекта pytest config. Ниже приведён простой пример плагина, который реализует фикстуру, повторно использующую ранее созданное состояние при последующих запусках pytest:
# content of test_caching.py
import pytest
def expensive_computation():
print("running expensive computation...")
@pytest.fixture
def mydata(pytestconfig):
cache = getattr(pytestconfig, "cache", None)
if cache is None:
# pytestconfig not having the cache attribute means the
# cache plugin is disabled.
expensive_computation()
return 42
val = cache.get("example/value", None)
if val is None:
expensive_computation()
val = 42
cache.set("example/value", val)
return val
def test_function(mydata):
assert mydata == 23
При первом запуске этой команды вы увидите вывод инструкции print:
$ pytest -q
F [100%]
================================= FAILURES =================================
______________________________ test_function _______________________________
mydata = 42
def test_function(mydata):
> assert mydata == 23
E assert 42 == 23
test_caching.py:26: 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:26: 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-9.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']
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-9.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 ===========================
Очистка содержимого кэша
Чтобы очистить все файлы и значения кэша, добавьте параметр --cache-clear следующим образом:
pytest --cache-clear
Это рекомендуется для запусков на серверах непрерывной интеграции, где изоляция и корректность важнее скорости.
Пошаговый запуск
В качестве альтернативы параметрам --lf -x, особенно если ожидается, что значительная часть набора тестов завершится неудачно, параметр --sw, --stepwise позволяет исправлять ошибки по одной. Набор тестов будет выполняться до первой ошибки, после чего запуск остановится. При следующем запуске тестирование продолжится с последнего теста, завершившегося неудачно, и будет идти до следующей ошибки. С помощью параметра --stepwise-skip можно пропустить один неудачный тест и остановить выполнение на втором. Это полезно, если вы застряли на неудачном тесте и хотите пока пропустить его. Указание --stepwise-skip также неявно включает --stepwise.
© 2015–2026 Holger Krekel and pytest-dev team
Licensed under the MIT License.
https://docs.pytest.org/en/stable/how-to/cache.html