⚡ Parametrize: один тест — десятки сценариев
⚠️ Урок устарел. Свежая версия в курсе pytest-basics: Parametrize: один тест — десятки сценариев.
Умножаем эффективность тестирования
⏱️ До: 10 похожих тестов = 50 строк кода, 10 минут на добавление кейса
⚡ После: 1 параметризованный тест = 10 строк кода, 30 секунд на новый кейс
✅ Один тест покрывает десятки сценариев с parametrize
✅ Понятные ids делают отчёт читаемым
✅ Ошибки проверяются так же, как happy path
Проблема: тесты-клоны
Пример копипасты, который нужно сократить:
# ❌ Дублирование — больно поддерживать
def test_add_simple_task():
assert add_task([], "buy milk") == ["buy milk"]
def test_add_task_strips_whitespace():
assert add_task([], " learn pytest ") == ["learn pytest"]
def test_add_task_with_special_chars():
assert add_task([], "email@example.com") == ["email@example.com"]Решение: один параметризованный тест
Переписываем в один параметризованный тест:
import pytest
@pytest.mark.parametrize(
"input_text,expected_output",
[
("buy milk", ["buy milk"]),
(" learn pytest ", ["learn pytest"]),
("email@example.com", ["email@example.com"]),
],
)
def test_add_task_variations(input_text, expected_output):
result = add_task([], input_text)
assert result == expected_outputВыгода: меньше кода, проще добавлять кейсы, отчёт понятен.
Понятные имена через ids
Добавляем читаемые ids для отчёта:
@pytest.mark.parametrize(
"input_text,expected_output",
[
("buy milk", ["buy milk"]),
(" learn pytest ", ["learn pytest"]),
],
ids=["simple_text", "strip_whitespace"], # 🏷️ читаемые названия в отчёте
)
def test_add_task_variations(input_text, expected_output):
...Несколько параметров
Параметризация нескольких аргументов:
@pytest.mark.parametrize("a, b, expected", [
(1, 2, 3),
(0, 0, 0),
(-1, 1, 0),
])
def test_add_numbers(a, b, expected):
assert a + b == expectedParametrize для ошибок
То же для ошибок через pytest.raises:
@pytest.mark.parametrize(
"text,expected_exception",
[
("", ValueError),
(None, TypeError),
(" ", ValueError),
],
)
def test_add_task_errors(text, expected_exception):
with pytest.raises(expected_exception):
add_task([], text)Практика: найдите баги с parametrize
Задача: почините валидацию email.
Практика: ловим баги в email-валидации одной таблицей кейсов:
# ❌ Баг: проверка слишком простая
def validate_email(email: str) -> bool:
return "@" in email
import pytest
@pytest.mark.parametrize(
"email,should_be_valid",
[
("user@example.com", True),
("invalid", False),
("user@.com", False), # 🎯 баг
("@example.com", False), # 🎯 баг
("user@example.", False), # 🎯 баг
("user+tag@example.com", True),
],
)
def test_validate_email_comprehensive(email, should_be_valid):
actual_result = validate_email(email)
assert actual_result == should_be_valid, f"Email: {email} | Expected: {should_be_valid}, Got: {actual_result}"🚀 Шпаргалка: parametrize в действии
Когда использовать
- ✅ Похожие кейсы с разными входными данными
- ✅ Граничные значения (0, отрицательные, очень большие)
- ✅ Комбинации параметров (цвет × размер × материал)
- ✅ Ошибки с разными невалидными входами
Мини-команды
Команды для быстрого запуска:
pytest -k "add_task" -v # Только нужные тесты
pytest -q --disable-warnings --maxfail=1 # Быстрый прогон с первым падениемЧеклист parametrize
- Похожие тесты объединены в
parametrize - Используются понятные
ids - Граничные случаи покрыты рядом кейсов
- Ошибки проверяются через
parametrize+pytest.raises
Дополнительно: комбинации параметров (опционально)
Дополнительно: пример с комбинированными параметрами:
@pytest.mark.parametrize("status", ["active", "inactive"])
@pytest.mark.parametrize("role", ["user", "admin"])
def test_user_combinations(status, role):
user = create_user(status=status, role=role)
assert user.has_access() == (status == "active" and role == "admin")Пишите тесты быстрее и точнее: меньше копипасты, больше покрытия и ясности. 🚀