Testing FastAPI Apps with pytest
FastAPI apps are easy to test because endpoints are plain functions behind an ASGI app, and dependencies can be swapped per test.
A first test
# test_books.py
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
def test_create_and_get_book():
res = client.post("/books", json={"title": "Dune", "author": "Frank Herbert"})
assert res.status_code == 201
book_id = res.json()["id"]
res = client.get(f"/books/{book_id}")
assert res.json()["title"] == "Dune"
def test_validation_error():
res = client.post("/books", json={"title": "No author"})
assert res.status_code == 422
pip install pytest
pytest -q
Overriding dependencies
import pytest
from main import app, get_current_user, get_db
@pytest.fixture
def client(test_db_session):
app.dependency_overrides[get_db] = lambda: test_db_session
app.dependency_overrides[get_current_user] = lambda: User(id=1, email="test@example.com", role="admin")
with TestClient(app) as c: # 'with' also runs lifespan startup/shutdown
yield c
app.dependency_overrides.clear()
def test_admin_can_delete(client):
assert client.delete("/articles/1").status_code == 204
Async tests
import pytest
from httpx import ASGITransport, AsyncClient
@pytest.mark.anyio
async def test_list_posts():
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as ac:
res = await ac.get("/posts")
assert res.status_code == 200
A reliable test database
- Run a real PostgreSQL in Docker or with Testcontainers for integration tests; SQLite behaves differently.
- Wrap each test in a transaction and roll it back, so tests don't affect each other.
- Use factory functions to create test data with sensible defaults.
Was this article helpful?
Comments
No comments yet. Have a question or a tip? Be the first to share it.