FastAPI dependency injection override: wrong DataFrame bug and fix
The test client started returning a DataFrame with twice the rows we expected. The endpoint looked fine locally, but the CI run printed a massive table. Only after inspecting the overridden dependency did we realize the cache was being reused.
from fastapi import FastAPI, Depends
from fastapi.testclient import TestClient
import pandas as pd
app = FastAPI()
def get_data():
# Original dependency hits the real DB
return pd.DataFrame({"id": [1, 2], "value": [10, 20]})
@app.get("/report")
async def report(df: pd.DataFrame = Depends(get_data)):
return {"rows": len(df), "data": df.to_dict(orient="records")}
# Test trying to override the dependency
def fake_data():
# FIXME: quick stub, but we forgot to reset cache
return pd.DataFrame({"id": [1], "value": [99]})
app.dependency_overrides[get_data] = fake_data
client = TestClient(app)
resp = client.get("/report")
print("len returned:", resp.json()["rows"]) # Expected 1, got 2
How to detect it:
# Diagnose the cache state
print(app.dependency_overrides)
# Output shows {<function get_data at 0x...>: <function fake_data at 0x...>}
# Check if the stub was called only once
The root cause is that FastAPI stores dependency overrides in a global cache that survives across TestClient instances. Assuming each TestClient gets a fresh override is wrong; the cached result is reused, so the stub runs only once and the original dependency later provides the full DataFrame. This behavior follows FastAPI’s design documented in the Depends section of the official docs.
The Fix
Quick Fix
# Reset the overrides before creating a new client
app.dependency_overrides = {}
app.dependency_overrides[get_data] = fake_data
client = TestClient(app)
When to use: Small test suites where speed matters.
Best Practice Solution
# Isolate overrides per test using a fixture
import pytest
@pytest.fixture
def client():
# Fresh app for each test prevents cross‑test bleed
test_app = FastAPI()
test_app.dependency_overrides[get_data] = fake_data
return TestClient(test_app)
def test_report(client):
resp = client.get("/report")
assert resp.json()["rows"] == 1
This pattern creates a new FastAPI instance per test, ensuring the dependency cache is never shared. It also makes the intent explicit, so future contributors won’t accidentally rely on stale overrides. The gotcha we hit was assuming FastAPI’s global state would be cleared automatically – it doesn’t.
- Overriding the dependency but reusing the same TestClient across multiple tests – the cache keeps the original result.
- Setting app.dependency_overrides after the TestClient has been instantiated – the client captures the previous state.
- Using a module‑level fixture that mutates the global overrides without resetting them between tests.
We switched to per‑test FastAPI instances and cleared the override cache. The CI pipeline now reports the correct row count and no longer flaps on the DataFrame size.
Related Issues
→ Why pandas assign vs inplace gives unexpected DataFrame → Fix How to resolve pip dependency conflicts in pandas projects → Why deepcopy on pandas DataFrames causes infinite recursion → Why pipx install pulls in global packages and breaks pandas