Skip to content

Testing FastAPI Applications

🎯 What You’ll Learn

  • The difference between unit tests and integration tests
  • How to set up pytest for FastAPI projects
  • How to use FastAPI’s TestClient to simulate requests
  • Best practices for structuring tests in production

🧠 Step 1: Unit vs Integration Tests

Type Purpose Example in FastAPI
Unit Test Test a single function or component in isolation Verify hash_password() returns a bcrypt hash
Integration Test Test multiple components working together Call /auth/login and check if JWT is returned

Both are important:

  • Unit tests catch logic errors early.
  • Integration tests ensure the whole system works correctly.

⚡ Step 2: Setting Up pytest

Install pytest:

pip install pytest

Project structure:

app/
  ...
tests/
  test_auth.py
  test_tasks.py

pytest automatically discovers files starting with test_.


⚡ Step 3: Using FastAPI’s TestClient

FastAPI provides TestClient (built on requests) to simulate API calls.

📄 tests/test_auth.py

from fastapi.testclient import TestClient
from app.main import app

client = TestClient(app)

def test_signup():
    response = client.post("/auth/signup", json={
        "username": "testuser",
        "email": "test@example.com",
        "hashed_password": "plaintextpassword"
    })
    assert response.status_code == 201
    data = response.json()
    assert "user_id" in data

def test_login():
    response = client.post("/auth/login", data={
        "username": "testuser",
        "password": "plaintextpassword"
    })
    assert response.status_code == 200
    data = response.json()
    assert "access_token" in data

🔍 What’s happening?

  • TestClient(app) spins up the FastAPI app in memory.
  • client.post() simulates HTTP requests.
  • Assertions check status codes and response data.

⚡ Step 4: Integration Test Example

📄 tests/test_tasks.py

from fastapi.testclient import TestClient
from app.main import app

client = TestClient(app)

def test_create_and_get_task():
    # First login to get token
    login = client.post("/auth/login", data={
        "username": "testuser",
        "password": "plaintextpassword"
    })
    token = login.json()["access_token"]

    headers = {"Authorization": f"Bearer {token}"}

    # Create a task
    response = client.post("/tasks/", json={"title": "My Task"}, headers=headers)
    assert response.status_code == 201
    task = response.json()
    assert task["title"] == "My Task"

    # Get tasks
    response = client.get("/tasks/", headers=headers)
    assert response.status_code == 200
    tasks = response.json()
    assert any(t["title"] == "My Task" for t in tasks)

🔍 Why this matters?

  • Tests the full flow: login → create task → fetch tasks.
  • Ensures JWT authentication and task ownership work together.

⚡ Step 5: Running Tests

Run all tests:

python -m pytest

Run a specific file:

python -m pytest tests/test_auth.py

Run with verbose output:

python -m pytest -v

🧠 Best Practices

  • Separate unit tests (pure functions) from integration tests (API endpoints).
  • Use fixtures for reusable setup (e.g. test database, test users).
  • Mock external services (e.g. email, payment APIs) to avoid slow tests.
  • Run tests in CI/CD pipelines before deployment.

🧪 Practice Challenge

  1. Write a unit test for verify_password() to ensure it rejects wrong passwords.
  2. Write an integration test for /admin/users that:
    • Logs in as a regular user → expects 403 Forbidden.
    • Logs in as an admin → expects a list of users.
  3. Add a fixture that sets up a temporary in-memory SQLite database for tests.

🧠 Recap

You now know how to:

  • Write unit and integration tests for FastAPI apps
  • Use pytest and TestClient to simulate requests
  • Structure tests for maintainability and production readiness