ashfaq1192 avatar

pytest-fastapi

Testing FastAPI applications with pytest using Test-Driven Development (TDD). Use when writing tests

作者 ashfaq1192|オープンソース

Pytest FastAPI Testing

Write and run tests for FastAPI applications using pytest, TestClient, and async testing patterns.

Quick Start

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:

pip install pytest pytest-asyncio httpx pytest-cov

Project structure:

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

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

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

def test_invalid_input(client):
    response = client.post("/items/", json={"invalid": "data"})
    assert response.status_code == 422

Async Endpoint Testing

@pytest.mark.asyncio
async def test_async_endpoint(async_client):
    response = await async_client.get("/async-data")
    assert response.status_code == 200

Dependency Override

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

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

def calculate_discount(price: float, percent: float) -> float:
    return price - (price * percent / 100)

3. Refactor: Improve While Tests Pass

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

# 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

# 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

# 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