Pytest FastAPI Testing
Write and run tests for FastAPI applications using pytest, TestClient, and async testing patterns.
Quick Start
python
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
def test_read_root():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Hello World"}
Setup
Install dependencies:
bash
pip install pytest pytest-asyncio httpx pytest-cov
Project structure:
code
project/
├── app/
│ ├── main.py
│ └── routers/
└── tests/
├── conftest.py # Copy from assets/conftest.py
└── test_*.py # Copy pattern from assets/test_api_template.py
Core Patterns
Testing CRUD Endpoints
python
def test_create(client, sample_data):
response = client.post("/items/", json=sample_data)
assert response.status_code == 201
assert "id" in response.json()
def test_read(client):
response = client.get("/items/1")
assert response.status_code == 200
def test_not_found(client):
response = client.get("/items/99999")
assert response.status_code == 404
Testing with Authentication
python
def test_protected_no_auth(client):
response = client.get("/protected/")
assert response.status_code == 401
def test_protected_with_auth(client, auth_headers):
response = client.get("/protected/", headers=auth_headers)
assert response.status_code == 200
Testing Validation
python
def test_invalid_input(client):
response = client.post("/items/", json={"invalid": "data"})
assert response.status_code == 422
Async Endpoint Testing
python
@pytest.mark.asyncio
async def test_async_endpoint(async_client):
response = await async_client.get("/async-data")
assert response.status_code == 200
Dependency Override
python
def test_with_mock_service(client):
def mock_service():
return MockService()
app.dependency_overrides[get_service] = mock_service
response = client.get("/service-endpoint")
app.dependency_overrides.clear()
assert response.status_code == 200
TDD Workflow
Follow the Red-Green-Refactor cycle:
1. Red: Write a Failing Test First
python
def test_calculate_discount():
# Write test for feature that doesn't exist yet
result = calculate_discount(price=100, percent=20)
assert result == 80.0 # This will fail - function doesn't exist
2. Green: Write Minimal Code to Pass
python
def calculate_discount(price: float, percent: float) -> float:
return price - (price * percent / 100)
3. Refactor: Improve While Tests Pass
python
def calculate_discount(price: float, percent: float) -> float:
"""Apply percentage discount to price."""
if not 0 <= percent <= 100:
raise ValueError("Percent must be between 0 and 100")
return round(price * (1 - percent / 100), 2)
TDD for FastAPI Endpoints
python
# Step 1: Write failing test for new endpoint
def test_search_items(client):
response = client.get("/items/search?q=test")
assert response.status_code == 200
assert "results" in response.json()
# Step 2: Implement minimal endpoint
@app.get("/items/search")
def search_items(q: str):
return {"results": []}
# Step 3: Refactor with actual search logic
Watch Mode for Rapid Feedback
bash
# Install pytest-watch for auto-rerun on file changes pip install pytest-watch # Run in watch mode (re-runs tests on save) ptw # Watch specific directory ptw tests/ # With verbose output ptw -- -v
Running Tests
bash
# Run all tests pytest # Run with verbose output pytest -v # Run specific file pytest tests/test_items.py # Run with coverage pytest --cov=app --cov-report=html # Run async tests pytest -v --asyncio-mode=auto
Resources
Templates (assets/)
- •conftest.py - Ready-to-use fixtures for TestClient, async client, database session, auth headers
- •test_api_template.py - Example test patterns for CRUD, validation, auth, and async testing
Copy templates to your tests/ directory and customize for your app.
Reference Documentation (references/)
See api_reference.md for:
- •Complete fixture patterns and scopes
- •Database testing with transaction rollback
- •Mocking strategies
- •Coverage configuration
See tdd_patterns.md for:
- •Detailed red-green-refactor examples
- •TDD workflow commands and watch mode
- •Common TDD patterns (Arrange-Act-Assert, factory fixtures)
- •Anti-patterns to avoid