Короткий ответ
Декоратор @pytest.mark.parametrize позволяет один раз описать тело теста и список пар (входные данные, ожидаемый результат), и pytest запустит тест отдельно для каждого набора.
Как это работает подробнее
В decorator передаются имена параметров строкой и список кортежей со значениями; pytest на их основе генерирует отдельный тест-кейс для каждой комбинации данных, видимый в отчёте как отдельный пункт. При падении одного набора остальные продолжают выполняться независимо, не блокируя проверку других значений.
Пример кода
testing_example_295.py
import pytest
def is_valid_age(age: int) -> bool:
return 0 <= age <= 120
@pytest.mark.parametrize("age,expected", [
(0, True),
(120, True),
(-1, False),
(121, False),
])
def test_is_valid_age(age, expected):
assert is_valid_age(age) is expectedЧто использовать на практике
- Практика. Параметризация особенно полезна для граничных значений и edge cases: пустая строка, отрицательное число, None, очень большое значение, которые иначе пришлось бы дублировать в отдельных функциях. Каждому кейсу можно задать понятный id через параметр ids, чтобы в отчёте CI сразу было видно, какой именно набор данных упал, не читая тело теста.
Подводные камни
- Риск. Запихивать в один параметризованный тест слишком разные сценарии с разной логикой проверки — тест становится нечитаемым и сложным для отладки при падении. Забывать, что параметризация не заменяет отдельные тесты для принципиально разных веток кода (например, успех и исключение), где сам assert и его смысл отличаются.