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 для читаемости.

Sources

Related topics