Skip to content

FastAPI Testing

Use this page as the FastAPI-specific testing guide for Wireup.

For general testing guidance, see Testing.

Basic Endpoint Testing

Use TestClient as a context manager so FastAPI lifespan runs.

from fastapi.testclient import TestClient


def test_endpoint(app: FastAPI):
    with TestClient(app) as client:
        response = client.get("/greet?name=World")
        assert response.status_code == 200

Override Dependencies in Tests

Use app-container overrides to inject fakes or mocks.

from fastapi.testclient import TestClient
from wireup.integration.fastapi import get_app_container


def test_override(app: FastAPI):
    class DummyGreeter(GreeterService):
        def greet(self, name: str) -> str:
            return f"Hi, {name}"

    with get_app_container(app).override.injectable(
        GreeterService, new=DummyGreeter()
    ):
        with TestClient(app) as client:
            response = client.get("/greet?name=Test")

    assert response.status_code == 200

Testing Request-Lifecycle Patterns

If tests depend on request-time helpers (middleware/decorators using @inject or get_request_container()), enable middleware_mode=True and run with TestClient context manager.

wireup.integration.fastapi.setup(container, app, middleware_mode=True)

with TestClient(app) as client:
    response = client.get("/requires-request-id")

Setup Timing in Tests

Best practice is to call setup(...) after routes are added. If setup is called earlier, lifespan must run before handling requests (again, use TestClient as a context manager).

Class-Based Handlers

Class-based handlers resolve constructor dependencies at startup. Apply overrides before creating TestClient.

import contextlib
from collections.abc import Iterator
from fastapi import FastAPI
from fastapi.testclient import TestClient
import wireup
import wireup.integration.fastapi
from wireup import InjectableOverride
from wireup.integration.fastapi import get_app_container


@contextlib.contextmanager
def create_test_app(
    overrides: list[InjectableOverride] | None = None,
) -> Iterator[FastAPI]:
    app = create_app()  # Create app, add routes, setup Wireup.

    with get_app_container(app).override.injectables(overrides or []):
        yield app


def test_user_handler_with_override():
    overrides = [
        InjectableOverride(target=UserProfileService, new=MockUserService())
    ]

    # Override first, then start app lifecycle.
    with create_test_app(overrides=overrides) as app:
        with TestClient(app) as client:
            response = client.get("/users/")

    assert response.status_code == 200

See Class-Based Handlers for more details.

Background Tasks

For task injection patterns and testing, see Background Tasks.

Common Test Failures

  • Injection not set up: verify wireup.integration.fastapi.setup(container, app) was called.
  • Request container unavailable: verify middleware_mode=True for request-time middleware/decorator patterns.
  • Lifespan not running: verify with TestClient(app) as client: usage.

For runtime/setup issues beyond tests, see Troubleshooting.