PythonJuniorLive coding
Что такое parametrize в pytest?
@pytest.mark.parametrize создаёт N тестов из одного — для каждого набора входных данных. Аргументы: имена параметров (строка или tuple) и список значений. Поддерживает ids для читаемых имён, pytest.param с marks, stacking для cartesian product, indirect через fixtures.
Базовый синтаксис
import pytest
@pytest.mark.parametrize(
("raw", "expected"),
[
(" Ada ", "ada"),
("Guido", "guido"),
("", ""),
],
)
def test_normalize(raw, expected):
assert raw.strip().lower() == expected
pytest развернёт это в три отдельных тест-кейса. В отчёте:
test_normalize[ Ada -ada] PASSED
test_normalize[Guido-guido] PASSED
test_normalize[-] PASSED
Кастомные ids
@pytest.mark.parametrize(
("payload", "status"),
[
({"email": "a@x"}, 200),
({}, 422),
({"email": "bad"}, 422),
],
ids=["valid", "missing_email", "invalid_email"],
)
def test_create(payload, status, client):
response = client.post("/users", json=payload)
assert response.status_code == status
В выводе: test_create[valid], test_create[missing_email] — сразу ясно, что упало.
pytest.param с marks и id
@pytest.mark.parametrize("value", [
1,
pytest.param(2, marks=pytest.mark.slow),
pytest.param(3, marks=pytest.mark.xfail(reason="bug #123")),
pytest.param(4, id="big-number"),
])
def test_thing(value):
assert value > 0
Так можно пропускать конкретный случай, помечать ожидаемо падающий, давать кастомный id.
Stacking — cartesian product
@pytest.mark.parametrize("locale", ["en", "ru"])
@pytest.mark.parametrize("currency", ["USD", "EUR"])
def test_format(locale, currency):
...
# 4 теста: (en,USD), (en,EUR), (ru,USD), (ru,EUR)
Indirect parametrization
Значения проходят через fixture перед попаданием в тест:
@pytest.fixture
def engine(request):
return create_engine(request.param)
@pytest.mark.parametrize("engine", ["sqlite:///:memory:", "postgresql://..."], indirect=True)
def test_with_db(engine):
assert engine is not None
Когда parametrize, когда несколько тестов
- parametrize — одна и та же логика, разные данные: парсинг, валидация, граничные случаи.
- Отдельные тесты — если каждый случай требует разной setup-логики или ассертов разной природы.
- property-based testing (hypothesis) — когда нужно покрыть огромное пространство значений.
Подводные камни
- Несоответствие числа имён параметров и элементов tuple —
CollectError. - Сложные объекты в качестве значений — pytest сгенерирует id вида
obj0,obj1. Дайте своиids. - Слишком много случаев в одном тесте — лог становится нечитаемым; разбейте по логически разным аспектам.
- Mutable объект в списке — изменение в одном тесте просочится в другие; копируйте.
- Параметризация над сетевыми/БД-fixtures без
indirect=True— pytest не сможет получить значение. - Уникальность ids: если генерируемые ids одинаковы, pytest добавит индексы — потеряете читаемость.
- Stacking над slow-фикстурой умножает время — иногда лучше один тест с loop внутри.
Common mistakes
- Дублировать одинаковые тесты вместо parametrize.
- Путать порядок параметров и значений.
- Не знать, что каждый набор — отдельный case.
What the interviewer is testing
- Пишет parametrize синтаксически правильно.
- Объясняет отдельные cases.
- Знает ids для читаемости.