dependency_overrides в FastAPI: как подменить зависимость и убрать подмену
Первый тест подменил текущего пользователя администратором, второй неожиданно тоже получил доступ. Код авторизации исправен: между тестами осталась запись в app.dependency_overrides. Это изменяемое состояние приложения, которое необходимо восстанавливать даже при падении assert.
Подменяется объект функции
В словаре overrides ключом служит функция, переданная в Depends. Другая функция с тем же именем не подходит. FastAPI найдёт замену при разборе графа зависимостей и вызовет её вместо исходной. Это удобно для внешних сервисов, текущего пользователя и временной базы, пока сам маршрут остаётся настоящим.
Не используйте безусловный clear, если тестовая инфраструктура уже установила общие подмены. Сохраните предыдущее содержимое и восстановите его в finally. Тогда локальный тест не удалит настройки, которые принадлежат внешней fixture. Ещё проще — отдельное приложение для каждого теста, когда его создание дешёво.
Проверим жизнь подмены целиком
Пример получает цену от зависимости. Внутри контекста цена тестовая, после выхода возвращается исходная. Нужны FastAPI и HTTPX. Менеджер контекста — обычный Python, но проверка выполняется запросами к FastAPI, поэтому она подтверждает настоящее подключение Depends.
Сохраните весь блок в файл и запустите Python 3.11+ в отдельном окружении с пакетами fastapi, httpx. Для моделей используется Pydantic v2; отдельный сервер для TestClient не нужен.
from contextlib import contextmanager
from typing import Annotated
from fastapi import FastAPI, Depends
from fastapi.testclient import TestClient
app = FastAPI()
def price_source():
return 100
@app.get("/price")
def price(value: Annotated[int, Depends(price_source)]):
return {"price": value}
@contextmanager
def override(app, dependency, replacement):
previous = dict(app.dependency_overrides)
app.dependency_overrides[dependency] = replacement
try:
yield
finally:
app.dependency_overrides.clear()
app.dependency_overrides.update(previous)
with TestClient(app) as client:
assert client.get("/price").json() == {"price": 100}
with override(app, price_source, lambda: 7):
assert client.get("/price").json() == {"price": 7}
assert client.get("/price").json() == {"price": 100}
try:
with override(app, price_source, lambda: 9):
raise RuntimeError("test interruption")
except RuntimeError:
pass
assert client.get("/price").json() == {"price": 100}
assert app.dependency_overrides == {}
print("override restored after normal exit and exception")Как оформить fixture
В pytest установите подмену до yield, а восстановление поместите в finally после него. Если fixture создаёт временный engine, его закрытие должно происходить после завершения клиента и операций приложения. Явный порядок освобождения упрощает диагностику закрытого соединения и утечек состояния.
Не запускайте параллельные тесты, которые одновременно меняют overrides у одного экземпляра app. Один тест может увидеть замену другого. Независимые приложения или изолированные процессы снимают эту проблему понятнее, чем блокировки вокруг общего словаря.
Что сохранить без подмены
Хотя бы один тест должен проверить исходную зависимость на её собственной границе. Подмена price_source проверяет использование цены маршрутом, но не получение цены из настоящего источника. Для сетевого клиента можно отдельно использовать локальный транспорт HTTPX и проверить URL, заголовки и обработку ошибок без внешней сети.
Если override не срабатывает, сравните идентичность функции в Depends и ключа словаря. Частая причина — зависимость обёрнута в другую функцию либо роутер импортирует другой экземпляр. Сначала выясните граф вызовов, затем меняйте подмену. Простое совпадение названия в исходнике не подтверждает, что объект один и тот же.
Следующая практика
В открытой практике fixtures и dependency override вы подмените источник данных для теста. Добавьте восстановление предыдущего словаря в финализатор и запустите два сценария подряд, чтобы увидеть изоляцию. Откройте урок fixtures и подмены зависимостей.
В бесплатной программе FastAPI подмены используются вместе с временной базой и HTTP-тестами. Пройдите эту часть целиком, чтобы fixture управляла и данными, и временем жизни приложения. Откройте программу курса.
Продолжить на практике
- FastAPI для начинающих: API с базой данных и тестами ↗
Соберёте свой первый полноценный backend на FastAPI: с базой данных, регистрацией, JWT-авторизацией и автотестами. 34 коротких урока и 140 заданий с проверкой — бесплатно.
