Как использовать skip и xfail для тестов, которые не могут завершиться успешно
Вы можете пометить тестовые функции, которые нельзя запустить на определённых платформах или которые, как вы ожидаете, завершатся ошибкой. Так pytest сможет обработать их соответствующим образом и вывести сводку тестового сеанса, сохранив при этом зелёный статус набора тестов.
Skip означает, что тест должен пройти только при соблюдении определённых условий; в противном случае pytest должен пропустить его запуск. Распространённые примеры: пропуск тестов только для Windows на платформах, отличных от Windows, или тестов, зависящих от внешнего ресурса, который сейчас недоступен (например, базы данных).
Xfail означает, что вы ожидаете, что тест завершится ошибкой по какой-либо причине. Распространённый пример — тест для ещё не реализованной функции или неисправленной ошибки. Если тест проходит, несмотря на то что ожидалась ошибка (помечен с помощью pytest.mark.xfail), это xpass, и он будет указан в сводке тестов.
pytest отдельно подсчитывает и перечисляет тесты со статусами skip и xfail. Подробные сведения о пропущенных тестах и тестах со статусом xfail по умолчанию не отображаются, чтобы не загромождать вывод. Чтобы посмотреть подробности, соответствующие «коротким» буквам, отображаемым при выполнении тестов, можно использовать параметр -r:
pytest -rxXs # show extra info on xfailed, xpassed, and skipped tests
Подробнее о параметре -r можно узнать, выполнив pytest -h.
(См. Встроенные параметры файла конфигурации)
Пропуск тестовых функций
Самый простой способ пропустить тестовую функцию — пометить её декоратором skip, которому можно передать необязательный reason:
@pytest.mark.skip(reason="no way of currently testing this") def test_the_unknown(): ...
Кроме того, пропустить тест во время его выполнения или настройки можно императивно, вызвав функцию pytest.skip(reason):
def test_function():
if not valid_config():
pytest.skip("unsupported configuration")
Императивный способ полезен, когда условие пропуска невозможно проверить во время импорта.
Также можно пропустить весь модуль, используя pytest.skip(reason, allow_module_level=True) на уровне модуля:
import sys
import pytest
if not sys.platform.startswith("win"):
pytest.skip("skipping windows-only tests", allow_module_level=True)
Справка: pytest.mark.skip
skipif
Если нужно пропускать что-либо при определённых условиях, используйте skipif. Вот пример пометки тестовой функции, которую нужно пропускать при запуске в интерпретаторе версии ниже Python 3.13:
import sys @pytest.mark.skipif(sys.version_info < (3, 13), reason="requires python3.13 or higher") def test_function(): ...
Если при сборе тестов условие имеет значение True, тестовая функция будет пропущена; указанная причина появится в сводке при использовании -rs.
Маркеры skipif можно использовать в нескольких модулях. Рассмотрим этот тестовый модуль:
# content of test_mymodule.py
import mymodule
minversion = pytest.mark.skipif(
mymodule.__versioninfo__ < (1, 1), reason="at least mymodule-1.1 required"
)
@minversion
def test_function(): ...
Маркер можно импортировать и повторно использовать в другом тестовом модуле:
# test_myothermodule.py from test_mymodule import minversion @minversion def test_anotherfunction(): ...
Для больших наборов тестов обычно удобно определить маркеры в одном файле, а затем последовательно применять их во всём наборе тестов.
Вместо логических значений можно использовать строковые условия, но их неудобно использовать совместно в нескольких модулях, поэтому они поддерживаются главным образом для обратной совместимости.
Справка: pytest.mark.skipif
Пропуск всех тестовых функций класса или модуля
Маркер skipif (как и любой другой маркер) можно использовать для классов:
@pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows")
class TestPosixCalls:
def test_function(self):
"will not be setup or run under 'win32' platform"
Если условие имеет значение True, этот маркер приведёт к пропуску каждого тестового метода этого класса.
Чтобы пропустить все тестовые функции модуля, используйте глобальную переменную pytestmark:
# test_module.py pytestmark = pytest.mark.skipif(...)
Если к тестовой функции применено несколько декораторов skipif, она будет пропущена, если истинно хотя бы одно из условий пропуска.
Пропуск файлов или каталогов
Иногда может потребоваться пропустить целый файл или каталог, например если тесты используют функции, доступные только в определённых версиях Python, или содержат код, который не нужно запускать с помощью pytest. В этом случае необходимо исключить файлы и каталоги из сбора тестов. Подробнее см. в разделе Настройка сбора тестов.
Пропуск при отсутствии импортируемой зависимости
Тесты при отсутствии импортируемой зависимости можно пропускать с помощью pytest.importorskip на уровне модуля, внутри теста или в функции настройки теста.
docutils = pytest.importorskip("docutils")
Если docutils не удаётся импортировать, тест будет пропущен. Также можно пропускать тесты в зависимости от номера версии библиотеки:
docutils = pytest.importorskip("docutils", minversion="0.3")
Версия будет прочитана из атрибута __version__ указанного модуля.
Краткое описание
Краткое руководство по пропуску тестов в модуле в разных ситуациях:
- Безусловный пропуск всех тестов в модуле:
pytestmark = pytest.mark.skip("all tests still WIP")
- Пропуск всех тестов в модуле при выполнении некоторого условия:
pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="tests for linux only")
- Пропуск всех тестов в модуле при отсутствии импортируемой зависимости:
pexpect = pytest.importorskip("pexpect")
XFail: пометка тестовых функций как ожидаемо завершающихся ошибкой
Маркер xfail можно использовать, чтобы указать, что вы ожидаете ошибку при выполнении теста:
@pytest.mark.xfail def test_function(): ...
Этот тест будет запущен, но при его ошибочном завершении трассировка не будет выведена. Вместо этого терминальный отчёт укажет его в разделах «ожидаемо завершившиеся ошибкой» (XFAIL) или «неожиданно прошедшие» (XPASS).
Кроме того, тест можно пометить как XFAIL императивно — из самого теста или его функции настройки:
def test_function():
if not valid_config():
pytest.xfail("failing configuration (but should work)")
def test_function2():
import slow_module
if slow_module.slow_function():
pytest.xfail("slow_module taking too long")
Эти два примера показывают ситуации, в которых не нужно проверять условие на уровне модуля, то есть когда в противном случае условие проверялось бы для маркеров.
Это приведёт к тому, что test_function XFAIL. Обратите внимание: после вызова pytest.xfail() никакой другой код не выполняется, в отличие от использования маркера. Это связано с тем, что внутри функция реализована через возбуждение специального исключения.
Справка: pytest.mark.xfail
Параметр condition
Если ожидается, что тест завершится ошибкой только при определённом условии, это условие можно передать первым параметром:
@pytest.mark.xfail(sys.platform == "win32", reason="bug in a 3rd party library") def test_function(): ...
Обратите внимание, что также необходимо передать причину (см. описание параметра в разделе pytest.mark.xfail).
Параметр reason
Причину ожидаемой ошибки можно указать с помощью параметра reason:
@pytest.mark.xfail(reason="known parser issue") def test_function(): ...
Параметр raises
Чтобы точнее указать причину ошибки теста, в аргументе raises можно задать одно исключение или кортеж исключений.
@pytest.mark.xfail(raises=RuntimeError) def test_function(): ...
Тогда, если тест завершится с исключением, не указанным в raises, он будет отмечен как обычная ошибка.
Параметр run
Если тест нужно пометить как xfail и отразить это в отчёте, но при этом вообще не запускать, задайте параметру run значение False:
@pytest.mark.xfail(run=False) def test_function(): ...
Это особенно полезно для тестов со статусом xfail, которые приводят к аварийному завершению интерпретатора и требуют последующего исследования.
Параметр strict
По умолчанию ни XFAIL, ни XPASS не приводят к ошибке всего набора тестов. Это поведение можно изменить, задав параметру только для ключевого слова strict значение True:
@pytest.mark.xfail(strict=True) def test_function(): ...
В этом случае результаты XPASS («неожиданно прошедшие») для этого теста будут приводить к ошибке всего набора тестов.
Значение параметра strict по умолчанию можно изменить с помощью параметра ini strict_xfail:
toml
[pytest] xfail_strict = true
ini
[pytest] strict_xfail = true
Игнорирование xfail
Указав в командной строке:
pytest --runxfail
можно принудительно запустить тест с маркером xfail и включить его в отчёт так, как будто этот маркер не был применён. В этом случае вызов pytest.xfail() также не даст никакого эффекта.
Примеры
Ниже приведён простой тестовый файл с несколькими вариантами использования:
from __future__ import annotations
import pytest
xfail = pytest.mark.xfail
@xfail
def test_hello():
assert 0
@xfail(run=False)
def test_hello2():
assert 0
@xfail("hasattr(os, 'sep')")
def test_hello3():
assert 0
@xfail(reason="bug 110")
def test_hello4():
assert 0
@xfail('pytest.__version__[0] != "17"')
def test_hello5():
assert 0
def test_hello6():
pytest.xfail("reason")
@xfail(raises=IndexError)
def test_hello7():
x = []
x[1] = 1
При запуске с параметром отчёта о тестах xfail вывод будет таким:
! pytest -rx xfail_demo.py =========================== test session starts ============================ platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR/example collected 7 items xfail_demo.py xxxxxxx [100%] ========================= short test summary info ========================== XFAIL xfail_demo.py::test_hello XFAIL xfail_demo.py::test_hello2 reason: [NOTRUN] XFAIL xfail_demo.py::test_hello3 condition: hasattr(os, 'sep') XFAIL xfail_demo.py::test_hello4 bug 110 XFAIL xfail_demo.py::test_hello5 condition: pytest.__version__[0] != "17" XFAIL xfail_demo.py::test_hello6 reason: reason XFAIL xfail_demo.py::test_hello7 ============================ 7 xfailed in 0.12s ============================
Skip/xfail с parametrize
При использовании parametrize можно применять маркеры, например skip и xfail, к отдельным экземплярам тестов:
import sys
import pytest
@pytest.mark.parametrize(
("n", "expected"),
[
(1, 2),
pytest.param(1, 0, marks=pytest.mark.xfail),
pytest.param(1, 3, marks=pytest.mark.xfail(reason="some bug")),
(2, 3),
(3, 4),
(4, 5),
pytest.param(
10, 11, marks=pytest.mark.skipif(sys.version_info >= (3, 0), reason="py2k")
),
],
)
def test_increment(n, expected):
assert n + 1 == expected
© 2015–2026 Holger Krekel and pytest-dev team
Licensed under the MIT License.
https://docs.pytest.org/en/stable/how-to/skipping.html