Как повторно запускать неудачные тесты и сохранять состояние между запусками тестов¶
Использование¶
Плагин предоставляет две опции командной строки для повторного запуска сбоев с последнего вызова 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
.