В цепочке зависимых фикстур pytest какая фикстура освобождается первой: зависимость или фикстура, использующая её?
Первой освобождается фикстура, которая зависит от другой, а затем — её зависимость. pytest выполняет очистку в обратном порядке относительно подготовки ресурсов: сначала закрываются ресурсы внутреннего уровня, затем внешние.
Модель фикстур решает типичную проблему тестов: ресурс нужно подготовить до проверки и гарантированно освободить после неё. В отличие от ручных вызовов очистки, pytest связывает жизненный цикл ресурса с фикстурой и учитывает зависимости между фикстурами.
Такой подход особенно важен для соединений с базой данных, транзакций, временных каталогов и серверов. Если зависимый ресурс закрыть после ресурса, от которого он зависит, очистка может обратиться к уже недоступному объекту.
Представим, что одна фикстура открывает соединение с базой данных, а другая создаёт транзакцию поверх этого соединения. Транзакцию необходимо завершить или откатить до закрытия соединения.
Неверный порядок очистки приводит к ошибкам teardown: откат транзакции может выполняться через закрытое соединение. Такие ошибки особенно неприятны тем, что основной тест уже мог завершиться успешно, а сбой появится только на этапе освобождения ресурсов.
Если фикстура transaction зависит от фикстуры connection, pytest сначала подготавливает connection, затем transaction. После теста порядок меняется на обратный: сначала выполняется очистка transaction, потом connection.
Для yield-фикстур код до yield является подготовкой, а код после yield — очисткой. Поэтому вложенные ресурсы нужно создавать через зависимости, а не рассчитывать на случайный порядок аргументов теста.
Для теста, использующего transaction, ожидаемый порядок сообщений: открытие соединения, начало транзакции, откат транзакции, закрытие соединения. Это гарантирует, что очистка зависимого ресурса происходит пока его зависимость ещё доступна.
Если подготовка фикстуры завершилась исключением до yield, её код после yield не выполняется, потому что ресурс считается не созданным. При этом уже успешно подготовленные зависимости всё равно освобождаются в обратном порядке.
Для независимых фикстур нельзя строить логику на предполагаемом порядке их очистки. Если порядок существенен, его следует выразить явной зависимостью между фикстурами.
Тесты сервиса используют фикстуру db_connection, а поверх неё — фикстуру db_transaction. Сначала разработчик сделал обе фикстуры независимыми и вручную вызывал очистку в тесте. При исключении в середине теста транзакция иногда не откатывалась, а соединение оставалось открытым.
Вариант с ручным try/finally в каждом тесте гарантирует очистку, но дублирует код и легко нарушается при добавлении новых сценариев. Независимые фикстуры с одинаковой областью действия проще объявить, но они не выражают требуемую зависимость, поэтому порядок очистки нельзя считать контрактом.
Выбранное решение — сделать транзакционную фикстуру зависимой от соединения и поместить откат после yield. В результате pytest автоматически выполняет откат до закрытия соединения, включая случаи, когда тело теста завершилось исключением.
Что произойдёт, если зависимая фикстура завершится исключением во время подготовки?
Её код очистки после yield не выполнится, поскольку до yield выполнение не дошло. Однако фикстуры, которые были успешно подготовлены раньше, например соединение с базой, будут освобождены pytest. Это позволяет безопасно очищать уже созданные ресурсы, не предполагая успешную подготовку всей цепочки.
Можно ли полагаться на порядок очистки двух независимых фикстур?
Нет, если между ними нет зависимости. pytest гарантирует обратный порядок для связанных фикстур, но независимые фикстуры не должны использовать порядок друг друга как неявный контракт. Если одна очистка требует доступности другой, это следует выразить через аргумент фикстуры.
Как область действия фикстуры влияет на порядок её очистки?
Область действия определяет, как долго экземпляр фикстуры может переиспользоваться и когда он будет освобождён, но не отменяет правило зависимостей. Для конкретного экземпляра зависимая фикстура освобождается раньше зависимости. При разных областях действия pytest также проверяет совместимость зависимостей: фикстура более широкой области не может напрямую зависеть от фикстуры более узкой области.