Как организовываются параметризованные тесты в PyTest?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Организация параметризованных тестов в PyTest
В PyTest параметризация тестов — это мощный механизм для запуска одного теста с различными наборами входных данных и ожидаемых результатов. Это позволяет избежать дублирования кода и повышает покрытие тестирования. Основные способы организации параметризации:
1. Декоратор @pytest.mark.parametrize
Это самый распространённый и гибкий способ. Декоратор принимает два аргумента: список названий параметров и список значений.
import pytest
# Простейший пример: параметризация одного аргумента
@pytest.mark.parametrize("number", [1, 2, 3, 4, 5])
def test_is_positive(number):
"""Проверяем, что все числа положительные"""
assert number > 0
# Параметризация нескольких аргументов
@pytest.mark.parametrize("input_value, expected", [
(5, 25), # 5² = 25
(0, 0), # 0² = 0
(-3, 9), # (-3)² = 9
(2.5, 6.25) # 2.5² = 6.25
])
def test_square(input_value, expected):
"""Тестируем возведение в квадрат"""
result = input_value * input_value
assert result == expected, f"Ошибка: {input_value}² = {result}, ожидалось {expected}"
2. Параметризация с помощью фикстур
PyTest позволяет создавать параметризованные фикстуры, которые могут передавать данные в тесты.
import pytest
# Создаем параметризованную фикстуру
@pytest.fixture(params=["chrome", "firefox", "edge"])
def browser(request):
"""Фикстура возвращает разные браузеры для тестирования"""
browser_name = request.param
print(f"\nЗапуск теста в браузере: {browser_name}")
return browser_name
def test_browser_compatibility(browser):
"""Тест будет запущен для каждого браузера"""
assert browser in ["chrome", "firefox", "edge"]
# Фикстура с более сложными данными
@pytest.fixture(params=[
{"username": "user1", "password": "pass123", "role": "admin"},
{"username": "user2", "password": "qwerty", "role": "user"},
{"username": "guest", "password": "", "role": "guest"}
])
def user_credentials(request):
"""Возвращает различные наборы учетных данных"""
return request.param
def test_authentication(user_credentials):
"""Тестируем аутентификацию с разными учетными данными"""
assert "username" in user_credentials
assert "role" in user_credentials
3. Динамическая параметризация
Иногда параметры нужно генерировать динамически во время выполнения.
import pytest
import math
# Генерация параметров через функцию
def generate_test_data():
"""Генерируем тестовые данные на лету"""
test_cases = []
for i in range(1, 6):
test_cases.append((i, i*i)) # (число, квадрат числа)
return test_cases
@pytest.mark.parametrize("num, square", generate_test_data())
def test_dynamic_parametrization(num, square):
"""Тест с динамически сгенерированными параметрами"""
assert num * num == square
# Параметризация на основе внешних данных
import json
import os
def load_test_cases():
"""Загружаем тест-кейсы из JSON файла"""
with open("test_data.json", "r") as f:
return json.load(f)
@pytest.mark.parametrize("test_case", load_test_cases())
def test_with_external_data(test_case):
"""Тест с данными из внешнего источника"""
assert test_case["input"] is not None
assert test_case["expected"] is not None
4. Комбинированная параметризация
Можно применять несколько декораторов parametrize для создания декартова произведения параметров.
import pytest
@pytest.mark.parametrize("x", [1, 2, 3])
@pytest.mark.parametrize("y", [10, 20])
def test_cartesian_product(x, y):
"""Тест будет выполнен 3 × 2 = 6 раз"""
result = x * y
assert result == x * y # Проверяем умножение
print(f"{x} * {y} = {result}")
5. Параметризация с идентификаторами
Для лучшей читаемости результатов тестирования можно задавать понятные идентификаторы.
import pytest
# Использование параметра ids для понятных имен тестов
@pytest.mark.parametrize(
"a, b, expected_sum",
[
(1, 2, 3),
(5, -5, 0),
(100, 200, 300),
(0, 0, 0)
],
ids=[
"positive_numbers",
"positive_and_negative",
"large_numbers",
"zeros"
]
)
def test_addition_with_ids(a, b, expected_sum):
"""Тест сложения с понятными именами кейсов"""
assert a + b == expected_sum
# Автоматическая генерация идентификаторов через функцию
def id_func(test_case):
"""Функция для генерации читаемых ID"""
a, b, expected = test_case
return f"{a}+{b}={expected}"
@pytest.mark.parametrize(
"a, b, expected",
[
(1, 2, 3),
(5, 5, 10),
(10, -3, 7)
],
ids=id_func # Используем функцию для генерации ID
)
def test_with_custom_ids(a, b, expected):
"""Тест с кастомными идентификаторами"""
assert a + b == expected
6. Параметризация классов
Можно параметризовать целые тестовые классы.
import pytest
@pytest.mark.parametrize("browser", ["chrome", "firefox", "safari"])
class TestLoginPage:
"""Весь класс будет параметризован для каждого браузера"""
def test_login_button(self, browser):
"""Тест кнопки логина"""
assert browser in ["chrome", "firefox", "safari"]
def test_username_field(self, browser):
"""Тест поля username"""
assert browser != "" # Простая проверка
def test_remember_me(self, browser):
"""Тест чекбокса 'Запомнить меня'"""
# Здесь могла бы быть реальная логика тестирования UI
print(f"Testing 'Remember me' in {browser}")
Ключевые преимущества параметризации в PyTest
- Уменьшение дублирования кода: Один тест покрывает множество сценариев
- Улучшенная читаемость: Все тестовые случаи видны в одном месте
- Гибкость: Поддержка статических и динамических данных
- Детальные отчёты: Каждый параметризованный запуск отображается отдельно
- Комбинирование: Возможность комбинировать параметризацию с фикстурами и другими декораторами
Рекомендации по использованию
- Именуйте параметры осмысленно — это улучшает читаемость кода
- Используйте идентификаторы (ids) для сложных параметров
- Избегайте излишней параметризации — слишком много комбинаций замедлит тесты
- Группируйте связанные тест-кейсы — логически связанные данные должны быть вместе
- Используйте внешние источники данных для больших наборов тестовых данных
Параметризация в PyTest — это мощный инструмент, который при правильном использовании значительно повышает эффективность и поддерживаемость тестового кода. Она позволяет создавать комплексные тестовые сценарии с минимальными усилиями и максимальным покрытием.