FASTAPI / 9. TESTING
Testing FastAPI Applications
Unit tests, integration tests, TestClient, dependency overrides
EXPLANATION
FastAPI is designed to be testable. The TestClient (from httpx) lets you send real HTTP requests to your app in tests without starting a server. Everything runs in-process.
Testing stack:
• pytest → test runner and fixture system
• httpx → the HTTP client TestClient is built on
• pytest-asyncio → for testing async routes directly
The TestClient pattern:
• client = TestClient(app) → creates a test HTTP client
• client.get("/path") → sends a real GET request to your app
• Returns a response object with .status_code, .json(), .headers
Dependency overrides — the killer feature for testing:
• Your routes depend on get_db() which talks to a real database
• In tests, you override get_db() with a test DB (SQLite in-memory)
• app.dependency_overrides[get_db] = override_get_db
• After tests, clear overrides: app.dependency_overrides.clear()
• This lets you test routes without a real DB, without real auth, without real email
What to test:
① Happy path → valid input, expected response, correct status code
② Error cases → 404, 401, 422 for bad input
③ Auth → protected routes reject unauthenticated requests
④ Edge cases → empty lists, null fields, boundary values
Fixtures (pytest) setup shared state:
• @pytest.fixture → defines reusable test setup
• yield in fixtures = setup before yield, teardown after
• Scope (function/module/session) controls how often setup runs
conftest.py holds shared fixtures — TestClient, test DB session — visible to all test files without import.ARCHITECTURE
TEST STRUCTURE:
tests/
├── conftest.py ← shared fixtures (client, db, auth token)
├── test_tasks.py ← task CRUD tests
├── test_auth.py ← login, signup, protected routes
└── test_users.py
DEPENDENCY OVERRIDE PATTERN:
Real app: Test app:
get_db() → PostgreSQL → get_db() → SQLite in-memory
get_current_user() → JWT → get_current_user() → fake user
app.dependency_overrides = {
get_db: lambda: test_db_session,
get_current_user: lambda: {"username": "testuser"}
}
TestClient sends real HTTP → routes execute → assertions on responseCODE