Pytest для AQA: углубленная практика

Практический курс для AQA инженеров, охватывающий структуру проектов, фикстуры и параметризацию [habr.com](https://habr.com/ru/companies/beget/articles/948806/). Вы научитесь применять Data Driven Testing, управлять маркировками и тестировать исключения [skyeng.ru](https://skyeng.ru/it-industry/programming/kak-ispolzovat-pytest-parametrize-v-testirovanii/).

1. Структура проекта и автоматическое обнаружение тестов

Структура проекта и автоматическое обнаружение тестов

Добро пожаловать в курс «Pytest для AQA: углубленная практика». Мы начинаем с фундамента — правильной организации кода. Хаотичная структура тестов — это бомба замедленного действия: сначала всё работает, но с ростом проекта поддержка превращается в ад. В этой статье мы разберем, как построить архитектуру, которая будет работать на вас, а не против вас.

Анатомия проекта и поиск тестов

Pytest обладает мощным механизмом автоматического обнаружения тестов (test discovery). Вам не нужно вручную регистрировать каждый тестовый файл, если вы следуете соглашениям об именовании.

Стандартная структура каталогов

Для средних и крупных проектов рекомендуется отделять код приложения от кода тестов. Типовая структура выглядит так:

Согласно habr.com, правильная структуризация помогает избежать запутанности и хрупкости тестов, когда изменение одного эндпоинта заставляет переписывать половину проекта.

Правила именования (Naming Conventions)

Чтобы Pytest автоматически нашел и запустил ваши тесты, соблюдайте следующие правила:

  • Файлы: должны начинаться с test_ или заканчиваться на _test.py (например, test_auth.py).
  • Классы: должны начинаться с Test (без метода __init__).
  • Функции: должны начинаться с test_.
  • Если вы назовете файл check_api.py, Pytest его проигнорирует, пока вы явно не укажете иное в конфигурации.

    Фикстуры: управление состоянием и данными

    Фикстуры (fixtures) — это сердце Pytest. Они заменяют методы setUp и tearDown из классического unittest, предоставляя более гибкий механизм внедрения зависимостей.

    Файл conftest.py

    Файл conftest.py служит для хранения фикстур, которые должны быть доступны в нескольких тестовых файлах. Вам не нужно импортировать фикстуры из conftest.py — Pytest делает это автоматически.

    > Pytest находит все тесты, которые лежат в директории tests. Внутри этого каталога можно создавать любую структуру. > > ru.hexlet.io

    Уровни видимости (Scope)

    Параметр scope в декораторе @pytest.fixture определяет, как часто будет создаваться фикстура. Это критически важно для оптимизации времени выполнения тестов.

    | Scope | Описание | | :--- | :--- | | function | По умолчанию. Создается заново для каждой тестовой функции. | | class | Создается один раз для тестового класса. | | module | Создается один раз для модуля (файла .py). | | session | Создается один раз за весь запуск тестов. Идеально для инициализации БД или браузера. |

    Yield-фикстуры: подготовка и очистка

    Для выполнения кода после теста (teardown) используется ключевое слово yield вместо return.

    В этом примере код до yield выполнится один раз перед первым тестом в модуле, а код после yield — один раз после завершения последнего теста в модуле.

    Параметризация: Data Driven Testing

    Часто нужно проверить одну и ту же логику с разными входными данными. Копировать тесты — плохая практика. Используйте декоратор @pytest.mark.parametrize.

    Если вы применяете несколько декораторов параметризации к одной функции, Pytest создаст декартово произведение всех параметров. Количество тестов рассчитывается по формуле:

    где — итоговое количество тестов, — количество вариантов в первом параметре, — количество вариантов во втором параметре. Например, если у вас 3 браузера и 2 типа пользователей, запустится тестов.

    Работа с Marks: управление запуском

    Маркировка (Marks) позволяет группировать тесты и управлять их выполнением.

    Кастомные метки

    Вы можете пометить тесты как smoke (дымовые) или regression (регрессионные). Чтобы избежать предупреждений (warnings), зарегистрируйте метки в pytest.ini:

    Использование в коде:

    Запуск только smoke-тестов: pytest -m smoke.

    Встроенные метки

    * @pytest.mark.skip(reason="..."): пропускает тест безусловно. * @pytest.mark.skipif(condition, reason="..."): пропускает тест при выполнении условия (например, sys.platform == 'win32'). * @pytest.mark.xfail: помечает тест как "ожидаемо падающий". Если он упадет — это не сломает билд (будет статус xfailed). Если пройдет — будет статус xpassed.

    Обработка исключений

    Для проверки негативных сценариев (например, API возвращает 404 или функция вызывает ошибку) используется контекстный менеджер pytest.raises.

    Параметр match принимает регулярное выражение для проверки текста ошибки.

    Практическое задание: Тестирование API-клиента

    Давайте объединим всё изученное. Представим, что у нас есть класс UserManager, который управляет пользователями.

    Исходный код (сохраните как src/user_manager.py или просто используйте в примере)

    Код тестов (tests/test_users.py)

    Итоги

  • Структура важна: Используйте tests/ и conftest.py для организации кода. Соблюдайте именование test_*.py.
  • Фикстуры вместо setUp: Используйте yield для очистки ресурсов и выбирайте правильный scope (function для изоляции, session для тяжелых ресурсов).
  • Параметризация: @pytest.mark.parametrize позволяет сократить код и покрыть больше кейсов данными.
  • Маркировка: Используйте smoke, skip и xfail для гибкого управления запуском тестов в CI/CD.
  • Исключения: Всегда проверяйте негативные сценарии через pytest.raises.
  • 2. Фикстуры: уровни видимости, yield и conftest.py

    Фикстуры: уровни видимости, yield и conftest.py

    В предыдущей лекции мы затронули тему структуры проекта. Теперь пришло время погрузиться в механизм, который делает Pytest самым популярным инструментом для тестирования в Python, — фикстуры (fixtures). Если тесты — это спектакль, то фикстуры — это рабочие сцены, которые устанавливают декорации, включают свет и убирают реквизит после занавеса.

    Философия фикстур: Dependency Injection

    В классическом unittest мы привыкли к методам setUp и tearDown. Pytest предлагает другой подход — внедрение зависимостей (Dependency Injection). Тест не должен знать, как создается база данных или браузер, он должен просто получить их готовыми к работе.

    Согласно документации, фикстуры обеспечивают надежность тестов и согласованность их результатов, инициализируя сервисы и состояния. > Фикстуры имеют явные имена и активируются путем их объявления в тестовых функциях. > > pytest-docs-ru.readthedocs.io

    Уровни видимости (Scopes): Скорость vs Изоляция

    Один из главных вопросов при проектировании автотестов — баланс между скоростью выполнения и чистотой данных. За это отвечает параметр scope в декораторе @pytest.fixture.

    1. Function (по умолчанию)

    Фикстура создается заново для каждого теста. Это гарантирует полную изоляцию: если один тест испортил данные пользователя, второй тест получит свежего пользователя.

    * Плюс: Максимальная надежность. * Минус: Медленно, если инициализация тяжелая (например, запуск браузера).

    2. Session

    Фикстура создается один раз за весь прогон тестов. Идеально подходит для глобальных настроек: подключение к БД, чтение конфига, авторизация администратора.

    Математика оптимизации

    Представим, что инициализация браузера занимает 5 секунд (), а сам тест идет 1 секунду (). У нас есть 10 тестов ().

    При scope='function' время выполнения () составит:

    где — количество тестов, — время подготовки, — время теста.

    Подставим числа: секунд.

    При scope='session' время () составит:

    где выполняется единожды, а — сумма времени выполнения всех тестов.

    Подставим числа: секунд. Мы ускорили тесты в 4 раза.

    3. Module и Class

    Промежуточные варианты. module — одна фикстура на файл .py, class — одна на тестовый класс.

    Yield: Setup и Teardown в одной функции

    Для очистки ресурсов (закрытие браузера, удаление записей из БД) используется ключевое слово yield. Всё, что написано до yield, — это Setup. Всё, что после, — Teardown.

    Важно: Код после yield выполнится, даже если сам тест упал с ошибкой. Это критично для AQA: мы не хотим оставлять висящие процессы браузеров или мусор в базе данных после упавшего теста.

    Conftest.py: Центр управления полетами

    Файл conftest.py позволяет расшарить фикстуры между множеством файлов без необходимости их импортировать.

    Иерархия conftest.py

    Вы можете иметь несколько файлов conftest.py в проекте. Pytest применяет их согласно вложенности папок.

    Согласно sky.pro, conftest.py — это не просто хранилище, а инструмент для создания масштабируемой архитектуры, позволяющий избежать дублирования кода.

    Продвинутые паттерны

    1. Фикстуры могут использовать другие фикстуры

    Это позволяет строить цепочки зависимостей.

    2. Autouse: Автоматический запуск

    Если добавить параметр autouse=True, фикстура выполнится автоматически для каждого теста в своей области видимости, даже если тест её не запрашивал. Это полезно для фоновых процессов (например, создание лог-файла или очистка временной папки перед тестом).

    Практический пример: Тестирование корзины покупок

    Реализуем сценарий, где нам нужно протестировать добавление товара в корзину. Нам понадобятся:

  • Пользователь (создается для каждого теста).
  • Товар (создается один раз на сессию, так как он не меняется).
  • Файл tests/conftest.py:

    Файл tests/test_cart.py:

    При запуске этого теста Pytest сначала создаст сессионный продукт (если он еще не создан), затем функционального пользователя, выполнит тест, удалит пользователя, и только после завершения всех тестов удалит продукт.

    Итоги

  • Фикстуры — это механизм подготовки окружения и данных, заменяющий устаревшие setUp/tearDown.
  • Scope определяет время жизни фикстуры. Используйте session для тяжелых ресурсов (БД, API-сессия) и function для изменяемых данных, требующих изоляции.
  • Yield разделяет этап инициализации и очистки. Код после yield выполняется гарантированно (кроме случаев фатального краша интерпретатора).
  • Conftest.py позволяет хранить фикстуры глобально. Pytest находит их автоматически, импортировать их не нужно.