FastAPI dependency_overrides tested offline with httpx ASGITransport: working versions (tested 2026-10-15)
Override FastAPI dependencies (including nested ones) and drive the app in-process with httpx.ASGITransport, no network and no running server.
passed6 passed · 0 failed2026-10-15 18:09 UTC- Status
- passed
- Last passed
- 2026-10-15 18:09 UTC
- Tests
- 6 / 6
- Language
- Python
- Runtime
- Python 3.13.16
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt .venv/bin/python -m pytest -q
Python 3.13.16 linux 6.17.0-1022-azure x64 runner: github-actions 38783276983
The code
Exactly the files that passed. Your agent can pull them with get_recipe("fastapi-dependency-overrides-httpx") (install), or see them on GitHub.
fastapi==0.143.2 httpx==0.28.1 pytest==9.1.1 pytest-asyncio==1.4.0
from __future__ import annotations
import sqlite3
from contextlib import asynccontextmanager
from fastapi import Depends, FastAPI, HTTPException
from pydantic import BaseModel
class Todo(BaseModel):
id: int
title: str
def create_connection() -> sqlite3.Connection:
conn = sqlite3.connect(":memory:", check_same_thread=False)
conn.row_factory = sqlite3.Row
conn.execute("CREATE TABLE todos (id INTEGER PRIMARY KEY, title TEXT)")
conn.execute("INSERT INTO todos (id, title) VALUES (1, 'write a recipe')")
conn.commit()
return conn
@asynccontextmanager
async def lifespan(app: FastAPI):
app.state.conn = create_connection()
try:
yield
finally:
app.state.conn.close()
app = FastAPI(lifespan=lifespan)
def get_conn() -> sqlite3.Connection:
return app.state.conn
class TodoRepo:
def __init__(self, conn: sqlite3.Connection):
self.conn = conn
def get(self, todo_id: int) -> Todo | None:
row = self.conn.execute(
"SELECT id, title FROM todos WHERE id = ?", (todo_id,)
).fetchone()
return Todo(id=row["id"], title=row["title"]) if row else None
def get_repo(conn: sqlite3.Connection = Depends(get_conn)) -> TodoRepo:
return TodoRepo(conn)
@app.get("/todos/{todo_id}", response_model=Todo)
def read_todo(todo_id: int, repo: TodoRepo = Depends(get_repo)) -> Todo:
todo = repo.get(todo_id)
if todo is None:
raise HTTPException(status_code=404, detail="todo not found")
return todo
import httpx
import pytest
from app import Todo, app, create_connection, get_conn, get_repo
@pytest.fixture(autouse=True)
def clean_overrides():
app.dependency_overrides.clear()
yield
app.dependency_overrides.clear()
def make_client() -> httpx.AsyncClient:
return httpx.AsyncClient(transport=httpx.ASGITransport(app=app), base_url="http://test")
class FakeRepo:
def __init__(self, todo: Todo | None):
self.todo = todo
def get(self, todo_id: int) -> Todo | None:
return self.todo if self.todo and self.todo.id == todo_id else None
async def test_asgi_transport_skips_lifespan():
async with make_client() as client:
with pytest.raises(AttributeError):
await client.get("/todos/1")
async def test_override_route_dependency():
app.dependency_overrides[get_repo] = lambda: FakeRepo(Todo(id=1, title="mocked"))
async with make_client() as client:
r = await client.get("/todos/1")
assert r.status_code == 200
assert r.json() == {"id": 1, "title": "mocked"}
async def test_override_parent_propagates_to_child():
app.dependency_overrides[get_conn] = create_connection
async with make_client() as client:
r = await client.get("/todos/1")
assert r.json()["title"] == "write a recipe"
async def test_missing_todo_returns_404():
app.dependency_overrides[get_repo] = lambda: FakeRepo(None)
async with make_client() as client:
r = await client.get("/todos/1")
assert r.status_code == 404
assert r.json()["detail"] == "todo not found"
async def test_generator_override_runs_teardown():
events: list[str] = []
def fake_repo():
events.append("open")
yield FakeRepo(Todo(id=1, title="yielded"))
events.append("close")
app.dependency_overrides[get_repo] = fake_repo
async with make_client() as client:
r = await client.get("/todos/1")
assert r.json()["title"] == "yielded"
assert events == ["open", "close"]
async def test_overrides_start_clean():
assert app.dependency_overrides == {}
[pytest] pythonpath = . asyncio_mode = auto
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.
Related stacks
FastAPI metrics with prometheus_client scraped as text
Instrument FastAPI with prometheus_client using a per-app registry, label by route template to bound cardinality, and scrape the text exposition format offline through httpx.ASGITransport.
FastAPI config via pydantic-settings as a cached dependency
Load config from env vars with pydantic-settings, inject it into FastAPI routes through a cached dependency, and test it offline with monkeypatch and dependency_overrides.
FastAPI endpoint with Pydantic v2 discriminated unions and Annotated validators
Accept a tagged union of payment methods with Field(discriminator), add Annotated validators, and test the exact 422 error shapes offline with TestClient.
FastAPI endpoints rate limited with slowapi, tested with TestClient
Add per-route and per-API-key rate limits to FastAPI with slowapi, then test 429 behavior offline with TestClient and the in-memory store.