27 октября 2026 года команда pytest выпустила версию 9.2.0. Это минорный выпуск, но в нём есть изменения, из-за которых старый тестовый код может упасть с ошибкой там, где раньше молча работал. Плюс заметное ускорение в CI и несколько предупреждений о том, что уберут в pytest 10.
Для тех, кто гоняет тесты в GitLab CI или GitHub Actions, главное здесь кэш перезаписанных модулей и новые коды выхода при сломанных плагинах. Ставится обычно: pip install -U pytest или uv add --dev pytest. Минимальная версия Python осталась 3.10, хотя сама 3.10 уже без поддержки.
Что может сломаться после обновления
Аргументы indirect, ids и scope в @pytest.mark.parametrize теперь передаются только по имени. Раньше частая ошибка выглядела так: написали parametrize("arg1", "arg2", [(1, 2)]), список значений попадал в indirect, и pytest ругался на несуществующую фикстуру (1, 2). Теперь сразу будет понятный TypeError. В аннотациях типов эти аргументы и так были именованными, так что код, который проходил mypy, не пострадает.
@pytest.mark.parametrize("a,b", [(1, 2), (3, 4)], ids=["small", "big"])
def test_sum(a, b):
assert a + b > 0
Ключ -c теперь падает с ошибкой использования, если путь к конфигу не существует, указывает на каталог или на файл неизвестного формата. Раньше такой путь либо игнорировался, либо вылетал необработанный FileNotFoundError.
Сломанные плагины больше не роняют запуск голым трейсбеком с кодом 1. Если плагин не найден через -p, pytest_plugins или PYTEST_PLUGINS, pytest завершится с кодом 4 (USAGE_ERROR), как при сломанном conftest.py. Если плагин найден, но падает при импорте, например из-за кривой точки входа pytest11 или недостающей зависимости, будет код 3 (INTERNAL_ERROR) с сохранённым трейсбеком. pytest.main() теперь возвращает эти коды, а не бросает ImportError. Если ваш CI различает коды выхода, это стоит учесть.
-c, parametrize и плагины. Источник: github.com, pytest 9.2.0.Что объявили устаревшим и уберут в pytest 10 или 11
- Декоратор
yield_fixtureзапланирован к удалению. Если он ещё где-то остался, меняйте на обычный@pytest.fixtureсyieldвнутри. - Фикстуры с областью
module,packageиsession, объявленные как методы экземпляра класса, теперь устарели. Для фикстур уровня класса это сделали ещё в 9.1. - Если в
[tool.pytest.ini_options]вpyproject.tomlстроковой опции передать не строку, появится предупреждениеPytestRemovedIn10Warning. В pytest 10 это будетTypeError. - Передавать
bytesвTerminalReporter.write_line()больше не стоит, нужна строка. Это касается авторов плагинов.
Длинные строки в ID тестов и проверка маркеров
Появилась опция parametrize_long_str_id_strategy: она решает, что делать с длинными строками и байтами в автоматических ID тестов. По умолчанию стоит short: значения длиннее 100 символов заменяются на имя аргумента и номер. Есть ещё sha256 (хеш значения), legacy (как было, всё значение целиком) и disallow (ошибка с просьбой задать ID вручную). Явные ID через ids=[...] или pytest.param(..., id=...) это не затрагивает. Если у вас где-то выбираются тесты по старым длинным ID, верните прежнее поведение:
[tool.pytest.ini_options] parametrize_long_str_id_strategy = "legacy"
При включённом strict_markers pytest теперь проверяет и имена маркеров в выражении -m. Опечатка вроде -m "slwo" больше не отберёт молча ноль тестов, pytest сразу укажет на неизвестный маркер. А в выводе полного диффа (Full diff) появилась легенда, какие строки помечены плюсом, а какие минусом.
Быстрее в CI: кэш assert по хешу исходника
pytest перезаписывает тестовые модули, чтобы assert показывал подробности, и кладёт результат в __pycache__. Раньше кэш сверялся по времени изменения файла. В CI после каждого checkout у всех файлов новое время, поэтому восстановленный кэш выбрасывался целиком и все модули перезаписывались заново. Теперь кэш проверяется по хешу исходника (формат pyc из PEP 552), это стоит около 15 микросекунд на файл. Старые кэши один раз пересоберутся.
Есть и другие ускорения. Захват вывода по умолчанию (--capture=fd) не читает буфер у тестов, которые ничего не печатали, а это заметно на больших наборах. Огромные диффы в assert теперь строятся лениво и сразу обрезаются, а RecursionError без найденного места рекурсии показывает только первые и последние 10 кадров вместо тысячи одинаковых. Полный стек по-прежнему доступен через --full-trace.
Мелочи, которые пригодятся
--no-summaryтеперь глушит только итог самого pytest. Сторонние плагины, например coverage, снова выводят свою сводку.- В
pytest_pluginsиPYTEST_PLUGINSможно писать имя точки входа установленного плагина, напримерxdist, как уже работало с-p. - Опции
junit_family,console_output_style,tmp_path_retention_policyи ещё несколько теперь проверяют значение. Опечатка в конфиге даёт короткую ошибку вместо молчаливого игнора, аpytest --helpпоказывает допустимые варианты. - Числовые опции вроде
log_level,max_warningsиfaulthandler_timeoutв TOML можно писать числами, без кавычек. - Типы
pytest.ParameterSetиpytest.Approxстали публичными, их можно использовать в аннотациях иisinstance.
Перед обновлением в рабочем проекте стоит прогнать тесты с -W error::pytest.PytestRemovedIn10Warning, проверить вызовы parametrize с позиционными ids или scope и посмотреть, как CI реагирует на коды выхода 3 и 4. В остальном 9.2 даёт более быстрый CI и меньше молчаливых ошибок в конфиге.