Сессионная фикстура pytest зависит от фикстуры с областью действия «на функцию». Что произойдёт при запуске тестов?
Pytest завершит подготовку такого теста с ошибкой ScopeMismatch. Сессионная фикстура не может напрямую зависеть от фикстуры с более коротким сроком жизни, потому что объект сессии оказался бы связан с уже уничтожаемой зависимостью.
Модель fixtures появилась в pytest как способ отделить подготовку окружения от самих тестов и повторно использовать эту подготовку. Области действия позволяют выбирать компромисс между скоростью: дорогие ресурсы создаются реже, — и изоляцией: состояние чаще пересоздаётся.
Чтобы такой механизм оставался предсказуемым, pytest учитывает не только область действия самой фикстуры, но и области действия всей цепочки её зависимостей.
Рассмотрим ситуацию: сессионная фикстура создаёт общий клиент, а для его настройки запрашивает function-scoped фикстуру. Клиент живёт всю тестовую сессию, тогда как его зависимость должна пересоздаваться для каждого теста.
Если разрешить такую зависимость, после первого теста сессионный объект мог бы хранить ссылку на очищенное, изменённое или уже недействительное состояние. Это создало бы скрытую зависимость между тестами и нарушило бы ожидаемый жизненный цикл ресурсов.
Правило pytest таково: фикстура не может зависеть от другой фикстуры с более узкой областью действия. Области обычно упорядочены от широкой к узкой: session, package, module, class, function.
Например:
При запросе api_client pytest обнаружит, что сессионная фикстура пытается получить function-scoped зависимость, и сообщит об ошибке ScopeMismatch. Это проверяется при построении графа зависимостей, до нормального выполнения теста.
Исправление зависит от требуемой изоляции. Если контекст действительно общий и неизменяемый, его можно сделать сессионным. Если контекст должен быть отдельным для каждого теста, клиент тоже следует сделать function-scoped или создавать его внутри более узкой фикстуры.
Нельзя надёжно решить проблему простым сохранением function-scoped объекта в глобальной переменной. Это обходит контроль pytest, но не устраняет очистку, гонки при параллельном запуске и загрязнение состояния.
В интеграционных тестах команда создавала сессионный клиент к тестовой службе. Его зависимостью был function-scoped объект с уникальным идентификатором запроса. Pytest выдавал ScopeMismatch, хотя разработчики рассчитывали переиспользовать сетевое соединение.
Рассматривались два варианта. Сделать идентификатор сессионным было быстро и сохраняло переиспользование клиента, но ухудшало изоляцию тестов. Сделать клиент function-scoped было безопаснее, однако увеличивало число подключений и время прогона.
Выбрали третий вариант: сессионным оставили только безопасный транспортный слой, а клиент, содержащий изменяемый контекст запроса, создавали на уровне функции. В результате соединение переиспользовалось, а данные и заголовки каждого теста оставались изолированными.
Нет. module уже уже, чем session, поэтому такая зависимость также вызовет ScopeMismatch. Направление допустимой зависимости должно идти от узкой фикстуры к более широкой, а не наоборот: function-scoped фикстура может использовать session-scoped ресурс.
Ошибки ScopeMismatch не будет, потому что области совместимы. Однако pytest создаст такую фикстуру отдельно для каждого теста, даже если её результат можно было бы переиспользовать.
Следовательно, корректность и производительность — разные вопросы. Для ускорения область действия можно расширить, но только после проверки, что объект не хранит изменяемое состояние между тестами и корректно очищается.
Да. Это допустимое направление зависимости: короткоживущий объект получает долгоживущий ресурс. Например, тестовая фикстура функции может использовать общий клиент или соединение, а затем очистить созданные именно ею данные.
Важно разделять жизненные циклы ресурса и состояния. Общий клиент не гарантирует изоляцию автоматически: тестовая фикстура должна сбрасывать контекст, удалять созданные записи или создавать уникальные идентификаторы.