Henry Hoang

Test folder structure template

Test folder structure template

Test folder structure template

  • Unit tests
  • Integration tests
  • Async tests
  • DB-mocked tests
  • JWT auth tests
  • Fixtures
  • Factories
  • Utilities

Recommended Test Folder Structure

tests/

├── __init__.py

├── conftest.py                 # Global pytest fixtures (DB, clients, settings)

├── factories/                  # Data builders / model factories
│   ├── __init__.py
│   └── user_factory.py

├── utils/                      # Shared testing utilities
│   ├── __init__.py
│   └── jwt_helpers.py

├── unit/                       # Pure unit tests (no DB, no FastAPI)
│   ├── __init__.py
│   └── test_helpers.py

├── integration/                # API + DB tests, Async tests
│   ├── __init__.py
│   ├── test_users.py
│   └── test_auth.py

└── e2e/                        # End-to-end tests (simulate full behavior)
    ├── __init__.py
    └── test_full_flow.py

What Each Folder Contains

tests/conftest.py

Global fixtures for:

  • Async test client
  • Mocked async SQLAlchemy database
  • Dependency overrides
  • JWT token helper fixture

Example:

import pytest
from httpx import AsyncClient
from app.main import app

@pytest.fixture
async def client():
    async with AsyncClient(app=app, base_url="http://test") as c:
        yield c

factories/

Used to create reusable model objects (factory pattern).

Example — user_factory.py:

def user_payload(username="alice", job="Engineer"):
    return {"username": username, "job": job}

utils/

Helper functions for repetitive tasks.

Example — jwt_helpers.py:

from app.auth import create_access_token

def get_token(user="test_user"):
    return create_access_token({"sub": user})

unit/

Pure Python tests, no FastAPI, no DB.

Example — test_helpers.py:

from app.utils.math import add

def test_add():
    assert add(2, 3) == 5

integration/

Tests FastAPI endpoints, async DB, JWT, etc.

Example — test_users.py:

import pytest

@pytest.mark.asyncio
async def test_create_user(client):
    response = await client.post("/users", params={"username": "bob", "job": "Manager"})
    assert response.status_code == 200

e2e/

Simulate full workflows across endpoints.

Example — test_full_flow.py:

@pytest.mark.asyncio
async def test_user_full_flow(client):
    # Create user
    res = await client.post("/users", params={"username": "eva", "job": "Dev"})
    assert res.status_code == 200

    # Fetch user
    res = await client.get("/users/eva")
    assert res.json()["job"] == "Dev"

Summary

This test structure supports:

FolderPurpose
unit/Fast, isolated logic tests
integration/DB + API + Async tests
e2e/Full workflow scenarios
factories/Model and payload generators
utils/Reusable test helpers
conftest.pyGlobal fixtures

On this page