При цепочке зависимостей фикстур как pytest определяет порядок их подготовки?
pytest строит граф зависимостей фикстур и подготавливает зависимость раньше фикстуры, которая её запрашивает. Поэтому при цепочке база данных → транзакция → клиент сначала создаётся база данных, затем транзакция, затем клиент. На порядок также влияют область действия фикстур и режим autouse, но сама зависимость задаёт обязательный порядок между связанными фикстурами.
Фикстуры появились как более гибкий способ подготовки тестового окружения по сравнению с большими процедурами общего setUp. Исходная проблема заключалась в том, что общую подготовку было трудно переиспользовать, комбинировать и корректно освобождать.
Модель зависимостей позволяет описывать не последовательность действий вручную, а требования каждой фикстуры. pytest затем сам определяет порядок подготовки и передаёт результат зависимости вызывающей фикстуре.
Представим, что тестовый клиент должен работать внутри транзакции, а транзакция требует готового соединения с базой. Если создать клиент раньше транзакции, он получит некорректное состояние или начнёт использовать ресурс вне нужного контекста.
Ручной вызов фикстур обычно является ошибочным решением: он обходит управление зависимостями, областями действия и очисткой со стороны pytest. Неявное использование глобального состояния создаёт другую проблему — тесты начинают зависеть от порядка запуска.
Зависимость объявляется параметром фикстуры. Когда тест запрашивает конечную фикстуру, pytest рекурсивно разрешает её зависимости, создавая каждую из них до потребителя.
В этом примере connection создаётся первым, затем transaction, затем client. Тест видит только client, но pytest автоматически подготавливает всю цепочку.
Область действия тоже значима: фикстуры с более широкой областью обычно подготавливаются раньше фикстур с более узкой областью, чтобы ресурс широкого уровня был доступен зависимым ресурсам. Нельзя бездумно делать фикстуру широкой области зависимой от узкой: области должны быть совместимы с жизненным циклом ресурсов.
Для фикстур с yield завершение происходит в обратном порядке относительно успешной подготовки цепочки: сначала освобождается ресурс, созданный последним. Это сохраняет корректность вложенных ресурсов.
Если между двумя фикстурами нет зависимости, pytest не обязан выбирать порядок, который следует считать частью контракта теста. Когда порядок важен, его нужно выразить параметром фикстуры, а не рассчитывать на расположение функций в файле.
В интеграционных тестах требовалось поднять контейнер с базой, открыть транзакцию и создать HTTP-клиент приложения. Рассматривались три варианта: собрать всё в одной фикстуре, использовать autouse для всех ресурсов или разделить ресурсы на зависимые фикстуры.
Единая фикстура была проще локально, но плохо переиспользовалась: тесту, которому нужна только база, приходилось создавать весь стек. autouse уменьшал число параметров тестов, однако скрывал зависимости и замедлял даже тесты, которым ресурсы не нужны.
Выбрали цепочку зависимых фикстур. База была доступна на уровне сессии, транзакция — на уровне теста, клиент — на уровне теста и зависел от транзакции. В результате порядок подготовки стал явным, лёгкие тесты не запускали лишние ресурсы, а изменение одного слоя не требовало переписывать остальные.
Что произойдёт, если две фикстуры одной области действия не зависят друг от друга, но тест использует обе?
Их взаимный порядок не следует считать гарантированным контрактом. pytest может выбрать порядок на основе других правил планирования, но тест не должен зависеть от конкретного результата. Если одна фикстура действительно требует эффекта другой, этот ресурс нужно передать через параметр зависимости.
В каком порядке освобождаются ресурсы зависимых фикстур?
Для успешно подготовленных фикстур очистка выполняется после завершения теста в обратном порядке: сначала потребитель, затем его зависимости. Например, сначала закрывается клиент, затем транзакция, затем соединение. Это позволяет внешнему ресурсу оставаться доступным, пока освобождается ресурс, созданный поверх него.
Почему фикстура с широкой областью действия не должна зависеть от фикстуры с более узкой областью?
Широкая фикстура живёт дольше узкой, поэтому после завершения узкой зависимости широкая фикстура могла бы остаться с недействительным объектом. pytest предотвращает такие несовместимые зависимости ошибкой ScopeMismatch. Решение — расширить область зависимости, сузить область потребителя или изменить архитектуру ресурсов так, чтобы долгоживущая фикстура не хранила краткоживущий объект.