This guide teaches unit testing for FastAPI applications using GitHub Copilot, Pytest, and Python’s unittest, focusing on refactoring and building a test suite.
In this guide, you’ll learn how to streamline unit testing in a FastAPI project using GitHub Copilot, Pytest, and Python’s built-in unittest. We’ll start by refactoring an existing FastAPI app that generates fake data, then build a robust test suite to ensure code quality and reliability.
Before refactor (Logic duplicated across 30+ files with multiple endpoints and Pydantic models.)After refactor
All core logic is consolidated into app/main.py, simplifying maintenance and improving testability:
Copy
Ask AI
# app/main.pyfrom fastapi import FastAPI, HTTPExceptionfrom pydantic import BaseModelimport sqlite3from typing import List, Dictfrom contextlib import contextmanagerfrom pathlib import Pathapp = FastAPI(title="Fake Data API")class DataRequest(BaseModel): count: intBASE_DIR = Path(__file__).resolve().parentDATA_DIR = BASE_DIR / "data" / "db"DB_PATH = DATA_DIR / "fakedata.db"DATA_DIR.mkdir(parents=True, exist_ok=True)@contextmanagerdef get_db_connection(): """ Context manager for SQLite connections. Yields: sqlite3.Connection """ conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row try: yield conn finally: conn.close()def fetch_fake_data(count: int) -> List[Dict]: """ Retrieve `count` random records from fake_data. """ with get_db_connection() as conn: cursor = conn.cursor() cursor.execute( """ SELECT first_name, last_name, email_address, age, city, occupation FROM fake_data ORDER BY RANDOM() LIMIT ? """, (count,), ) rows = cursor.fetchall() return [dict(row) for row in rows]@app.post("/api/v1/getfakedata", response_model=List[Dict])async def get_fake_data_endpoint(request: DataRequest) -> List[Dict]: """ Endpoint to fetch fake data. Raises HTTPException for invalid counts or database errors. """ count = request.count if count <= 0: raise HTTPException(status_code=400, detail="Count must be greater than 0") if count > 1000: raise HTTPException(status_code=400, detail="Count must not exceed 1000") try: data = fetch_fake_data(count) if not data: raise HTTPException(status_code=404, detail="No data found") return data except sqlite3.Error as e: raise HTTPException(status_code=500, detail=f"Database error: {e}")if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)
Consolidating your endpoints and database logic makes it easier to write targeted unit tests and debug issues.