← Назад к вопросам

Приведи пример генератора в Pytest

1.0 Junior🔥 161 комментариев
#Автоматизация тестирования#Инструменты тестирования

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Пример генератора данных в Pytest

В Pytest под термином «генератор» часто подразумевают не классические генераторы Python (хотя их тоже можно использовать), а фикстуры (fixtures) с параметризацией, которые динамически генерируют данные для тестов. Это позволяет создавать гибкие и переиспользуемые источники тестовых данных.

Однако, если говорить о классических Python-генераторах (функциях с ключевым словом yield), то они также активно применяются в Pytest, особенно в составе фикстур. Рассмотрим оба подхода.

1. Генератор как фикстура с параметризацией (наиболее частая практика)

Вы можете создать фикстуру, которая возвращает функцию-генератор данных или использует yield для управления жизненным циклом. Но чаще под «генерацией» понимают динамическое создание параметров.

Пример: Фикстура, генерирующая тестовые данные

import pytest
import json

# Фикстура, которая "генерирует" разные конфигурации для теста
@pytest.fixture(params=[
    {"user": "admin", "access": "full"},
    {"user": "editor", "access": "partial"},
    {"user": "viewer", "access": "readonly"}
])
def user_config(request):
    """Генератор конфигураций пользователей. Pytest вызовет тест для каждого param."""
    config = request.param
    config["active"] = True  # Динамически добавляем поле
    yield config  # Используем yield как финализатор (опционально)
    # Здесь можно, например, логировать завершение теста для этой конфигурации
    print(f"Тест завершен для пользователя: {config['user']}")

def test_user_permissions(user_config):
    """Тест будет запущен 3 раза с разными user_config."""
    assert "user" in user_config
    assert user_config["active"] is True
    # Проверяем логику в зависимости от сгенерированных данных
    if user_config["user"] == "admin":
        assert user_config["access"] == "full"

2. Фикстура как классический генератор Python (с yield)

Такие фикстуры часто используются для setup/teardown логики, но они также могут «генерировать» объекты по запросу.

import pytest

@pytest.fixture
def dynamic_data_generator():
    """Фикстура-генератор, которая отдает данные по одному, имитируя поток."""
    data_points = [10, 20, 30, 40, 50]
    
    def inner_generator():
        for point in data_points:
            yield point * 2  # Генерация модифицированных данных
    
    # Возвращаем сам генератор, чтобы тест мог им управлять
    return inner_generator()

def test_with_generator(dynamic_data_generator):
    """Тест, использующий переданный генератор напрямую."""
    results = list(dynamic_data_generator)
    assert results == [20, 40, 60, 80, 100]
    # Или можно итерироваться по генератору в тесте
    gen = dynamic_data_generator
    first_value = next(gen)
    assert first_value == 20

3. Параметризация тестов с помощью pytest.mark.parametrize и генераторов

Это самый мощный способ «генерации» данных. Вы можете использовать выражение-генератор (generator expression) прямо в декораторе.

import pytest

# Генерация пар вход-ожидание через генераторное выражение
def generate_test_cases():
    """Функция, возвращающая генератор кортежей (input, expected)."""
    for i in range(5):
        yield i, i * 2  # Классический генератор Python

# Преобразуем генератор в список для parametrize (требуется статический список)
test_data = list(generate_test_cases())

@pytest.mark.parametrize("input_value, expected", test_data)
def test_multiplication(input_value, expected):
    """Тест будет запущен 5 раз с данными из генератора."""
    assert input_value * 2 == expected

4. Динамическая параметризация (pytest_generate_tests)

Для наиболее сложной динамической генерации параметров на этапе коллекции тестов используется хук pytest_generate_tests.

import pytest

def generate_dynamic_params():
    """Генерируем параметры на основе внешних условий, например, данных из файла."""
    # Имитируем чтение из файла или БД
    environments = ["dev", "stage", "prod"]
    for env in environments:
        yield env.upper(), env  # Каждый yield — это один набор аргументов

def pytest_generate_tests(metafunc):
    """Хук Pytest для динамической параметризации."""
    if "env_name" in metafunc.fixturenames and "env_code" in metafunc.fixturenames:
        # Преобразуем генератор в список кортежей
        params = list(generate_dynamic_params())
        # Применяем параметризацию
        metafunc.parametrize("env_name, env_code", params)

def test_environment_config(env_name, env_code):
    """Тест запустится 3 раза для DEV, STAGE, PROD."""
    assert env_name == env_code.upper()
    print(f"Запуск теста для окружения: {env_code}")

Ключевые выводы

  • Основной механизм «генерации» в Pytest — это параметризация (@pytest.mark.parametrize или params в фикстурах).
  • Классические генераторы Python (yield) в Pytest чаще используются:
    *   В фикстурах для разделения кода setup и teardown.
    *   Для создания источников данных, которые можно превратить в список для параметризации.
  • Для максимальной гибкости при динамическом создании данных используйте хук pytest_generate_tests.
  • Все подходы направлены на одну цель: избежать дублирования кода и обеспечить полное покрытие различных входных данных и состояний системы.

Таким образом, «генератор в Pytest» — это, как правило, не один конкретный объект, а стратегия использования механизмов Pytest (фикстуры, параметризация) вместе с генераторами Python для эффективного создания разнообразных тестовых данных.