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.yield для очистки ресурсов и выбирайте правильный scope (function для изоляции, session для тяжелых ресурсов).@pytest.mark.parametrize позволяет сократить код и покрыть больше кейсов данными.smoke, skip и xfail для гибкого управления запуском тестов в CI/CD.pytest.raises.