Unit 09.02: Overriding the expensive dependency
Overriding the dependency is what lets the suite run with no key, no network and no budget.
The whole path, without the expensive part
An endpoint depending on a model, with the dependency replaced for the test.
The code counts how many times the real one was built.
from fastapi import Depends, FastAPI
from fastapi.testclient import TestClient
CALLS = []
def get_model():
CALLS.append("real")
return {"name": "real-model"}
app = FastAPI()
@app.post("/classify")
def classify(model=Depends(get_model)) -> dict:
return {"category": "billing", "model": model["name"]}
app.dependency_overrides[get_model] = lambda: {"name": "fake-model"}
client = TestClient(app)
print(client.post("/classify").json())
print(f"real model built {len(CALLS)} time(s)")
app.dependency_overrides.clear()
print("""
The whole request path ran and the expensive dependency did not. That is the
payoff for injecting the model rather than importing it: the suite needs no
key, no network and no budget.
""")
The real dependency was never built. Everything else ran - the routing, the response shaping, the error handling - so the test covers the same code the production request will take.
Clearing the override afterwards matters: overrides are global to the app object, so one left in place silently affects every later test.
The mistake this prevents
The mistake is skipping these tests when no key is available, marking them as integration tests. The endpoint logic is not integration - only the model call is, and overriding it separates the two cleanly.
Takeaway
Override the expensive dependency and clear it afterwards. The full request path runs; only the part that costs money is replaced.
