Типизация в pytest
Примечание
Предполагается, что читатель знаком с системой типизации Python и её преимуществами.
Дополнительную информацию см. в документации по типизации Python.
Зачем нужны тесты с типизацией?
Типизация тестов даёт значительные преимущества:
- Читаемость: чётко определяет ожидаемые входные и выходные данные, повышая читаемость, особенно в сложных тестах и тестах с параметризацией.
- Рефакторинг: это главное преимущество типизации тестов: она значительно упрощает рефакторинг, позволяя средству проверки типов указать на необходимые изменения как в рабочем коде, так и в тестах, без необходимости запускать весь набор тестов.
Типизация рабочего кода также помогает обнаруживать некоторые ошибки, которые могут быть не выявлены тестами вовсе (независимо от покрытия), например:
def get_caption(target: int, items: list[tuple[int, str]]) -> str:
for value, caption in items:
if value == target:
return caption
Средство проверки типов правильно сообщит об ошибке, что функция может вернуть None, однако даже набор тестов с полным покрытием может не выявить этот случай:
def test_get_caption() -> None:
assert get_caption(10, [(1, "foo"), (10, "bar")]) == "bar"
Обратите внимание: приведённый выше код имеет покрытие 100%, но ошибка не обнаруживается (конечно, пример «очевидный», но он помогает проиллюстрировать мысль).
Использование типизации в наборах тестов
Чтобы указать типы фикстур в pytest, просто добавьте обычные аннотации типов к функциям фикстур — особых действий из-за декоратора fixture не требуется.
import pytest
@pytest.fixture
def sample_fixture() -> int:
return 38
Аналогичным образом фикстуры, передаваемые тестовым функциям, должны быть аннотированы типом возвращаемого значения фикстуры:
def test_sample_fixture(sample_fixture: int) -> None:
assert sample_fixture == 38
С точки зрения средства проверки типов не имеет значения, что sample_fixture на самом деле является фикстурой, управляемой pytest; важно лишь то, что sample_fixture — это параметр типа int.
Та же логика применима к @pytest.mark.parametrize:
@pytest.mark.parametrize("input_value, expected_output", [(1, 2), (5, 6), (10, 11)])
def test_increment(input_value: int, expected_output: int) -> None:
assert input_value + 1 == expected_output
Та же логика применима при указании типов функций фикстур, принимающих другие фикстуры:
@pytest.fixture
def mock_env_user(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("USER", "TestingUser")
Заключение
Добавление типизации в тесты pytest повышает ясность, упрощает отладку и сопровождение, а также обеспечивает безопасность типов. Эти практики позволяют создать надёжный, понятный и удобный в сопровождении набор тестов, который лучше подготовлен к будущим изменениям и сводит к минимуму риск ошибок.
© 2015–2026 Holger Krekel and pytest-dev team
Licensed under the MIT License.
https://docs.pytest.org/en/stable/explanation/types.html