Master FastAPI Testing: Unit & Integration Tests with Pytest & HTTPX
This guide walks you through setting up a FastAPI testing environment, creating a project structure, writing both unit and integration tests using pytest, httpx, and FastAPI's TestClient, and running the tests to ensure reliable, maintainable applications.
Testing is a crucial part of software development that ensures your application runs as expected and remains maintainable. FastAPI is a modern web framework that works well with popular testing libraries for comprehensive testing.
1. Set up testing environment
First, install the required packages. We will use pytest for testing, httpx for sending HTTP requests, and fastapi.testclient for testing FastAPI applications.
<code>pip install pytest httpx</code>2. Create project structure
Below is a basic FastAPI project layout:
<code>my_project/
├── app/
│ ├── __init__.py
│ ├── main.py
│ ├── models.py
│ ├── crud.py
│ ├── schemas.py
│ ├── database.py
│ └── tests/
│ ├── __init__.py
│ ├── test_main.py
│ └── test_crud.py
├── requirements.txt
└── README.md</code>3. Example FastAPI application
Create a simple FastAPI app in main.py :
<code>from fastapi import FastAPI, HTTPException
app = FastAPI()
items = {"foo": {"name": "Foo", "description": "A test item"}}
@app.get("/items/{item_id}")
async def read_item(item_id: str):
if item_id not in items:
raise HTTPException(status_code=404, detail="Item not found")
return items[item_id]
@app.post("/items/{item_id}")
async def create_item(item_id: str, item: dict):
if item_id in items:
raise HTTPException(status_code=400, detail="Item already exists")
items[item_id] = item
return item</code>4. Write unit tests
Unit tests focus on individual components, such as CRUD functions, without running the whole application.
<code>import pytest
from app.crud import get_item, create_item
items = {"foo": {"name": "Foo", "description": "A test item"}}
def test_get_item():
item = get_item(items, "foo")
assert item["name"] == "Foo"
assert item["description"] == "A test item"
def test_get_item_not_found():
with pytest.raises(KeyError):
get_item(items, "bar")
def test_create_item():
new_item = {"name": "Bar", "description": "Another test item"}
create_item(items, "bar", new_item)
assert "bar" in items
assert items["bar"] == new_item
def test_create_item_already_exists():
with pytest.raises(ValueError):
create_item(items, "foo", {"name": "Foo", "description": "Duplicate item"})
</code>5. Write integration tests
Integration tests verify that different components work together. Use httpx and fastapi.testclient to send requests to your endpoints and check responses.
<code>from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
def test_read_item():
response = client.get("/items/foo")
assert response.status_code == 200
assert response.json() == {"name": "Foo", "description": "A test item"}
def test_read_item_not_found():
response = client.get("/items/bar")
assert response.status_code == 404
assert response.json() == {"detail": "Item not found"}
def test_create_item():
new_item = {"name": "Bar", "description": "Another test item"}
response = client.post("/items/bar", json=new_item)
assert response.status_code == 200
assert response.json() == new_item
def test_create_item_already_exists():
new_item = {"name": "Foo", "description": "Duplicate item"}
response = client.post("/items/foo", json=new_item)
assert response.status_code == 400
assert response.json() == {"detail": "Item already exists"}
</code>6. Run tests
Use pytest to discover and run all tests. Navigate to your project directory and execute:
<code>pytest</code>This will find and run all files and functions whose names start with test_ .
7. Conclusion
Testing is essential for ensuring application reliability and maintainability. FastAPI combined with pytest and httpx provides a powerful framework for writing both unit and integration tests, allowing you to verify that your FastAPI application behaves as expected.
Code Mala Tang
Read source code together, write articles together, and enjoy spicy hot pot together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.