Set Up the Test Fixture
Exit

Set Up the Test Fixture

Create a pytest fixture that gives each test an isolated in-memory SQLite database

💻

Writing code and entering commands is only available on desktop. Open this page on a larger screen to complete this chapter.

What the fixture does

The client fixture sets up a fresh database for each test, injects it into the app, and tears it down when the test finishes:

@pytest.fixture(name="client")
def client_fixture():
    engine = create_engine("sqlite://", connect_args={"check_same_thread": False})
    SQLModel.metadata.create_all(engine)

    def get_session_override():
        with Session(engine) as session:
            yield session

    app.dependency_overrides[get_session] = get_session_override
    client = TestClient(app)
    yield client
    app.dependency_overrides.clear()

  • create_engine("sqlite://") — creates an in-memory database. No file on disk.
  • SQLModel.metadata.create_all(engine) — creates the expense table in memory.
  • get_session_override — a local generator function that yields sessions from the in-memory engine. This replaces get_session from database.py during tests.
  • app.dependency_overrides[get_session] = get_session_override — tells FastAPI to use get_session_override instead of get_session for any request made through this client.
  • yield client — the fixture pauses here and hands the TestClient to the test. When the test finishes, the fixture resumes.
  • app.dependency_overrides.clear() — removes the override so it does not affect other test runs.

You can see database.py and main.py in the read-only tabs — the fixture imports from both.

Instructions

Create the client fixture in test_main.py.

  1. Add these imports, each on its own line:
    • pytest
    • TestClient from fastapi.testclient
    • Session and SQLModel and create_engine from sqlmodel
    • get_session from database
    • app from main
    • Expense from models
    • ValidationError from pydantic
  2. Define a function called client_fixture with the @pytest.fixture(name="client") decorator.
  3. Inside client_fixture, create engine = create_engine("sqlite://", connect_args={"check_same_thread": False}).
  4. Call SQLModel.metadata.create_all(engine) — this creates the expense table in the in-memory database.
  5. Define a nested function called get_session_override. Inside it, open a with Session(engine) as session: block and yield session.
  6. Set app.dependency_overrides[get_session] = get_session_override.
  7. Create client = TestClient(app).
  8. yield client.
  9. After the yield, call app.dependency_overrides.clear().