Skip to main content

Testing FastAPI Dependency Injection: Where to Start

· 6 min read
Serhii Hrekov
software engineer, creator, artist, programmer, projects founder

For beginners, testing FastAPI dependencies should start with one fundamental goal: isolation. You must ensure your route logic is tested without making real network calls, hitting a live database, or relying on complex configuration settings.

The key to achieving this is using FastAPI's built-in app.dependency_overrides dictionary. This allows you to replace any real dependency function with a simple, predictable mock function for the duration of a test.

Steps to Writing Your First Dependency Test

The following examples use pytest as the testing framework and httpx.Client for making synchronous test requests.

Step 1: Set up the Application and Client

First, define the core application structure and a reusable client fixture.

import pytest
from httpx import Client
from fastapi import FastAPI, Depends, Header, HTTPException
from typing import Annotated

# --- Application and Dependencies ---
app = FastAPI()

# Our original dependency (the one we want to mock)
def get_config():
"""Reads configuration from a real source."""
# Imagine this reads from environment variables or a config file
return {"log_level": "DEBUG", "api_key": "REAL_KEY_123"}
Config = Annotated[dict, Depends(get_config)]

# A route that uses the dependency
@app.get("/status")
def get_status(config: Config):
return {"status": "online", "key_prefix": config["api_key"][:4]}


# --- Test Setup ---
@pytest.fixture(scope="module")
def client():
# Use httpx.Client to interact with the FastAPI app instance
with Client(app=app, base_url="http://test") as client:
yield client

Step 2: Write the Simple Mock Override

Define a replacement function that returns the specific data you need for the test.

# The mock function must have the same signature (or be callable with no args)
def override_get_config():
"""Mock implementation returning predictable test data."""
return {"log_level": "MOCK", "api_key": "TEST_MOCK_456"}

Step 3: Test Dependency Success (Injecting Mock Data)

Map the original dependency to the mock override, run the test, and check the result.

def test_config_override_success(client: Client):
# 1. Apply the override: Map the original function to the mock function
app.dependency_overrides[get_config] = override_get_config

# 2. Run the test request
response = client.get("/status")

# 3. Assert: Check if the response uses the mock data
assert response.status_code == 200
assert response.json()["key_prefix"] == "TEST"

# 4. Cleanup (Crucial!): Clear the override after the test
app.dependency_overrides = {}

Step 4: Test Dependency Failure (Forcing Exceptions)

If your dependency enforces security or validation, you can replace it with a mock that always raises the expected HTTPException.

# A dependency that fails the request early
def check_required_header(api_key: Annotated[str, Header(alias="X-Test-Key")]):
if api_key != "SECRET":
raise HTTPException(status_code=401)
return True

@app.get("/secure", dependencies=[Depends(check_required_header)])
def get_secure_data():
return {"data": "secret"}

# The mock override that simulates a bypass
def override_check_required_header():
return True

# The mock override that forces the failure
def fail_check_required_header():
raise HTTPException(status_code=401, detail="Mocked Denied")


def test_override_failure(client: Client):
# Apply the mock that forces the failure
app.dependency_overrides[check_required_header] = fail_check_required_header

response = client.get("/secure")

# Check that the request was stopped by the dependency and returned 401
assert response.status_code == 401
assert response.json()["detail"] == "Mocked Denied"

app.dependency_overrides = {}

Step 5: The Cleanup Rule (Automating Reset)

Manually clearing overrides after every test (app.dependency_overrides = {}) is error-prone. In real-world projects, you should use a fixture to automate this cleanup using yield.

@pytest.fixture(autouse=True) # runs before and after every test
def clean_overrides():
# Code before yield (runs before test)
yield
# Code after yield (runs after test, regardless of pass/fail)
app.dependency_overrides = {}

# Annotation: By using an autouse fixture, you simplify individual tests
# as you no longer need to manually add the cleanup line in every test function.

Sources and Further Reading

  1. FastAPI Documentation - Testing with Dependencies
  2. Python httpx Documentation - TestClient
  3. Python pytest Documentation - Fixtures and Scopes
  4. FastAPI Documentation - Dependencies with yield (for complex mocking)