Skip to main content
In this lesson you’ll learn how to build reliable unit tests for a FastAPI service using pytest and how to structure your code so tests run deterministically. We demonstrate a refactor from a scattered multi-file layout into a single, testable main.py and then add unit tests that mock the database layer. You can accelerate test scaffolding with GitHub Copilot; always review any generated tests.
A presentation slide titled "Creating Unit Tests" with a dark curved shape on the right containing the word "Demo" in bright blue. A small copyright notice ("© Copyright KodeKloud") appears in the bottom left.

Overview

  • Goal: Make endpoints and DB access easy to test by isolating business logic.
  • Approach: Refactor into a single app/main.py that contains:
    • A Pydantic request model
    • A context manager for SQLite connections
    • A fetch_fake_data helper returning rows as dictionaries
    • A POST endpoint /api/v1/getfakedata with input validation and error handling
  • Test strategy: Use pytest + fastapi.testclient.TestClient and patch DB calls to keep tests fast and deterministic.
Related resources:

Original (messy) example

This example illustrates the problem of splitting routing and logic across nested modules — making tests harder to write and maintain.
Refactoring into a single module simplifies test setup and makes it clear where to patch for unit tests.

Refactored, testable implementation (app/main.py)

The following is a self-contained FastAPI app that uses SQLite and provides a helper to fetch random rows. It includes validation, explicit errors, and a context manager for DB connections.
Note: The endpoint expects a JSON body like {"count": 2} and is available at POST /api/v1/getfakedata.
Before running tests, ensure you have the required test packages installed, for example: pip install pytest pytest-mock fastapi[all]. The tests below use pytest, unittest.mock.patch, and FastAPI’s TestClient.

Test strategy

  • Unit tests should avoid hitting a real database — patch sqlite3.connect or fetch_fake_data so tests stay fast and deterministic.
  • Use fastapi.testclient.TestClient to exercise endpoints.
  • Keep tests close to the module for small projects (e.g., app/test_main.py) or use a top-level tests/ directory for larger projects.

Example pytest file (app/test_main.py)

Place this file next to app/main.py. It demonstrates:
  • Mocking the DB connection for fetch_fake_data
  • Patching fetch_fake_data when testing the endpoint behavior
  • Verifying validation and error responses

Test cases matrix

When listing example JSON payloads or code snippets in tables, wrap them in backticks, for example: use {"count": 2} as the request body.

Run tests

From project root run:
  • Discover and run tests with pytest:
    • pytest
Example successful output:

Tips and best practices

  • Keep business logic (e.g., fetch_fake_data) separate from framework glue (FastAPI route handlers) so you can unit test logic without spinning up the full app.
  • For unit tests: mock external state (DB, network) to keep tests fast and deterministic.
  • For integration tests: use a disposable test database and clean up state using fixtures.
  • Use TestClient for endpoint-level tests and patch the underlying helpers for unit-style tests.
  • When using GitHub Copilot to scaffold tests: treat outputs as a starting point — verify correctness, edge cases, and error handling.
Avoid running unit tests against your production database. Use mocks for unit tests and a separate ephemeral/test DB for integration tests to prevent data corruption or flaky results.

Wrap up

  • Refactoring to a single, well-structured module simplifies testing and reduces coupling between layers.
  • Use pytest with FastAPI’s TestClient and mock the database layer to write fast, reliable unit tests.
  • Leverage tools like GitHub Copilot to generate test scaffolding quickly, but always review generated code to ensure accurate assertions and coverage.
Further reading and references:

Watch Video