Что можно и нужно выносить в фикстуры при тестировании с помощью pytest?

Ответ

Фикстуры в pytest — это мощный механизм для инжекции зависимостей и управления состоянием тестов. В них следует выносить код, который подготавливает окружение, данные или ресурсы, необходимые для выполнения тестов.

Типичные сценарии для фикстур:

  1. Инициализация и очистка внешних зависимостей:

    • Подключение к базе данных, кэшу или внешнему API.
    • Запуск и остановка Selenium WebDriver, мобильного эмулятора.
    • Создание и удаление временных файлов или директорий.
    import pytest
    import sqlite3
    import tempfile
    import os
    
    @pytest.fixture
    def temp_db():
        """Создает временную БД в памяти для тестов."""
        conn = sqlite3.connect(':memory:')
        yield conn  # Тест использует соединение
        conn.close()  # Очистка после теста
    
    @pytest.fixture
    def config_file():
        """Создает временный конфигурационный файл."""
        with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
            f.write('{"timeout": 10}')
        yield f.name  # Возвращает путь к файлу
        os.unlink(f.name)  # Удаляет файл после теста
  2. Подготовка тестовых данных:

    • Генерация мок-объектов, стабов.
    • Заполнение БД фиктивными данными (fixtures).
    • Создание сложных объектов доменной модели.
    @pytest.fixture
    def mock_user():
        """Возвращает объект пользователя с тестовыми данными."""
        return User(id=1, username='test_user', is_active=True)
  3. Конфигурация тестового окружения:

    • Установка параметров (timeout, URL, режимы логирования).
    • Пропуск тестов при определенных условиях (например, отсутствие драйвера).
  4. Общие шаги для группы тестов:

    • Авторизация пользователя перед выполнением сценариев.
    • Переход на определенную страницу веб-приложения.

Ключевые параметры фикстур:

  • scope: Определяет время жизни фикстуры ("function" (по умолчанию), "class", "module", "session"). Используйте scope="session" для тяжелых операций (запуск браузера), чтобы не повторять их для каждого теста.
  • autouse=True: Фикстура применяется автоматически ко всем тестам в своей области видимости, без явного вызова.
  • params: Позволяет параметризовать фикстуру, запуская тест несколько раз с разными данными.

Главное правило: Если код setup/teardown повторяется в нескольких тестах — его стоит вынести в фикстуру. Это делает тесты чище, DRY (Don't Repeat Yourself) и упрощает их поддержку.

Ответ 18+ 🔞

А, фикстуры в pytest! Ну это ж, блядь, святое дело, ебать мои старые костыли! Это не просто какой-то там setup-teardown, это целая философия, как не нагородить хуйни в каждом втором тесте.

Представь: ты пишешь тесты, и в каждом из них одно и то же — подключись к базе, создай файлик, сгенерируй тестового юзера. И так двадцать раз. Ну ёпта, это же пиздец какой-то рутины! Вот для этого и придумали фикстуры. Выносишь всю эту подготовительную еблу в одно место, и потом просто тычешь в тест аргументом с таким же названием. Красота, блядь!

Так, где их, эти фикстуры, применять-то, спросишь ты?

  1. Всякая инициализация и уборка за собой. Ну, типа, открыл соединение с базой — закрой. Создал временный файл — удали. Запустил браузер — прибей. Чтобы не засирать систему, как последний свин.

    import pytest
    import sqlite3
    import tempfile
    import os
    
    @pytest.fixture
    def temp_db():
        """База в памяти. Быстро и чисто."""
        conn = sqlite3.connect(':memory:')
        yield conn  # Вот тут тест работает
        conn.close()  # А вот тут мы подтираем за собой
    
    @pytest.fixture
    def config_file():
        """Временный конфиг. Создали, отдали, стёрли. Как одноразовый стаканчик."""
        with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
            f.write('{"timeout": 10}')
        yield f.name  # Путь к файлу — тесту
        os.unlink(f.name)  # Файл — в помойку истории
  2. Подготовка тестовых данных. Чтобы не копипастить один и тот же словарь или объект на десять страниц. Создал фикстуру mock_user — и все тесты его юзают. И если надо поменять, меняешь в одном месте, а не бегаешь, как угорелый, по всем файлам.

    @pytest.fixture
    def mock_user():
        """Вот тебе юзер. Бери и тестируй. Не благодари."""
        return User(id=1, username='test_user', is_active=True)
  3. Настройка окружения. Сказал фикстуре scope="session", и она один раз за прогон всех тестов что-то тяжёлое поднимет (типа того же браузера), а не будет это делать перед каждым тестом, тратя время и нервы.

  4. Общие шаги для кучи тестов. Например, перед всеми API-тестами надо залогиниться и получить токен. Выносишь в фикстуру с autouse=True в рамках модуля — и все тесты уже авторизованы. Волшебство, блядь!

Параметры, на которые стоит смотреть:

  • scope: Это, сука, важно! Не тащи тяжёлую фикстуру с scope="function" (это по умолчанию). Для браузера или подключения к реальной БД ставь scope="session" — один раз на всю сессию тестов. Экономия времени — овердохуища!
  • autouse=True: Включил — и фикстура сама применяется ко всем тестам в своей области видимости. Не надо её явно передавать. Удобно, но опасно — можно не заметить, откуда ноги растут.
  • params: Хочешь прогнать тест на разных данных? Параметризуй фикстуру! Она вернёт разные значения, и тест запустится для каждого.

А главное правило, которое бьёт прямо в глаз: если один и тот же кусок кода setup/teardown повторяется больше одного раза — ты уже мудак, если не выносишь его в фикстуру. Это называется DRY, чувак, Don't Repeat Yourself. Не повторяй себя, а то заебешься потом поддерживать эту копипасту. Фикстуры — твои лучшие друзья в мире тестов, запомни это.