Introduction to pytest

Getting started with FastAPI tests

Testing is a crucial part of software development, and FastAPI makes it easy to write tests for your applications using Pytest. In this article, we'll walk you through the process of setting up Pytest fixtures for your FastAPI app, allowing you to run tests efficiently and effectively.

What is a Pytest fixture?

A Pytest fixture is a function that provides a fixed baseline for running tests. Fixtures can be used to set up resources, such as database connections or API clients, that are needed by multiple tests. They help to keep your test code clean and modular, making it easier to maintain and understand.

Running async tests with Pytest

FastAPI is an asynchronous web framework, so it's essential to run your tests using an asynchronous backend. To do this, create a Pytest fixture with the scope="session" parameter, which ensures that the fixture is only run once for the entire test session. This fixture should return the string "asyncio", which tells FastAPI to use the built-in asyncio framework for running async tests.

@pytest.fixture(scope="session")
def anyio_backend():
    return "asyncio"

Getting the fastapi.TestClient to receive requests

The fastapi.TestClient is a powerful tool for testing your FastAPI app without having to start the server. To create a fixture for the test client, define a generator function that yields an instance of TestClient with your app as an argument. Then, use the @pytest.fixture() decorator to mark the function as a fixture.

@pytest.fixture()
def client() -> Generator:
    yield TestClient(app)

Clearing the database between tests

To ensure that your tests don't interfere with each other, it's essential to clear the database between test runs. Create an async generator function that clears the relevant tables in your database and then yields. Use the @pytest.fixture(autouse=True) decorator to mark the function as a fixture that should be automatically used for every test.

@pytest.fixture(autouse=True)
async def db() -> AsyncGenerator:
    post_table.clear()
    comment_table.clear()
    yield

The httpx.AsyncClient for making requests

To make requests to your FastAPI app during tests, you'll need an async client. The httpx.AsyncClient is a perfect choice for this task. Create an async generator function that takes the client fixture as a parameter and sets up an instance of AsyncClient with your app and the base URL of the test client. Then, use the @pytest.fixture() decorator to mark the function as a fixture.

@pytest.fixture()
async def async_client(client) -> AsyncGenerator:
    async with AsyncClient(app=app, base_url=client.base_url) as ac:
        yield ac

This fixture uses dependency injection, which means that when you include async_client as a parameter in your tests, Pytest will automatically provide the value of the fixture for you.

With these fixtures in place, you're now ready to start writing tests for your FastAPI app. Remember that testing is an essential part of software development, and using Pytest fixtures with FastAPI can help you write clean, efficient, and effective tests for your application.

New files

storeapi/tests/conftest.py
from typing import AsyncGenerator, Generator

import pytest
from fastapi.testclient import TestClient
from httpx import AsyncClient
from storeapi.main import app
from storeapi.routers.posts import comments_table, post_table


@pytest.fixture(scope="session")
def anyio_backend():
    return "asyncio"


@pytest.fixture()
def client() -> Generator:
    yield TestClient(app)


@pytest.fixture(autouse=True)
async def db() -> AsyncGenerator:
    post_table.clear()
    comments_table.clear()
    yield


@pytest.fixture()
async def async_client(client) -> AsyncGenerator:
    async with AsyncClient(app=app, base_url=client.base_url) as ac:
        yield ac