This project includes an independent Locust load testing suite located in the locust/ directory. It has its own virtual environment and dependencies, completely separate from the backend application.
Locust is an open-source load testing tool that allows you to define user behavior in Python code and simulate thousands of concurrent users hitting your API.
The load testing suite covers the following API areas:
| Task Set | Weight | Description |
|---|---|---|
PostsTasks |
4 | Blog CRUD operations (heaviest workload) |
UsersTasks |
3 | User listing and profile access |
BackgroundTasksTasks |
2 | Background task creation and status checks |
TiersTasks |
1 | Tier listing (low frequency) |
AuthTasks |
1 | Login/logout cycles |
locust/
├── config.py # Configuration (host, credentials, API prefix)
├── helpers.py # Shared utilities (login, token management)
├── locustfile.py # Main entry point - defines the simulated user
├── poetry.toml # Poetry local configuration
├── pyproject.toml # Dependencies (independent from backend)
└── tasks/
├── __init__.py # Exports all task sets
├── auth.py # Authentication task set
├── posts.py # Blog posts task set
├── tasks.py # Background tasks task set
├── tiers.py # Tiers task set
└── users.py # Users task set
- Python 3.11+ installed
- Poetry installed (
pip install poetry==1.7.1) - The backend API running (default:
http://127.0.0.1:8000) - A configured
backend/.envfile (Locust reads credentials from it)
From the root project directory, run:
python3 setup.pySelect option 3 - Load Testing (Locust) and the CLI will handle the setup and execution automatically.
- Navigate to the
locust/directory:
cd locust- Install dependencies (creates an independent
.venv):
poetry install- Run Locust:
poetry run locust- Open the Locust web UI at http://localhost:8089.
The locust/config.py file reads configuration from environment variables (loaded from backend/.env):
| Variable | Default | Description |
|---|---|---|
LOCUST_HOST |
http://127.0.0.1:8000 |
Target API host |
ADMIN_EMAIL |
admin@admin.com |
Admin email for authentication |
ADMIN_PASSWORD |
admin |
Admin password for authentication |
You can override the target host directly when running Locust:
poetry run locust --host http://your-api-host:8000poetry run locustAccess http://localhost:8089 and configure:
- Number of users (peak concurrency)
- Spawn rate (users started per second)
- Host (target URL, pre-filled from config)
Run without the web UI for CI/CD pipelines:
poetry run locust --headless -u 100 -r 10 --run-time 60sOptions:
-u 100: Simulate 100 concurrent users-r 10: Spawn 10 users per second--run-time 60s: Run for 60 seconds
poetry run locust --headless -u 50 -r 5 --run-time 30s --html report.html- Create a new file in
locust/tasks/(e.g.,my_feature.py):
from locust import TaskSet, task
class MyFeatureTasks(TaskSet):
@task
def my_endpoint(self):
self.client.get("/api/v1/my-feature")- Export it in
locust/tasks/__init__.py:
from .my_feature import MyFeatureTasks- Add it to
locustfile.pywith a weight:
tasks = {
# ...existing tasks...
MyFeatureTasks: 2,
}Key metrics to monitor:
- RPS (Requests Per Second): Throughput of your API
- Response Time (median/p95/p99): Latency distribution
- Failure Rate: Percentage of failed requests
- Number of Users: Current concurrent users
| Metric | Acceptable | Warning | Critical |
|---|---|---|---|
| p95 Response Time | < 500ms | 500ms - 2s | > 2s |
| Failure Rate | < 1% | 1% - 5% | > 5% |
| RPS | Depends on infra | - | - |
Ensure the backend API is running before starting Locust:
# From the backend directory
poetry run uvicorn src.main:app --reload --host 0.0.0.0 --port 8000Verify that backend/.env contains valid ADMIN_EMAIL and ADMIN_PASSWORD values and that the admin user has been created (run migrations first).
Make sure you're running from the locust/ directory and have installed dependencies:
cd locust
poetry install- The Locust suite uses a separate virtual environment from the backend to avoid dependency conflicts.
- Configuration is loaded from
backend/.envso you don't need to duplicate credentials. - The
wait_time = between(1, 3)inlocustfile.pysimulates realistic user think time between requests.