~/aglili

Load Testing With Locust

Published: 1/10/2026 (Updated: 1/10/2026)

What is Load Testing?

Load testing is a form of performance testing that makes it easy to study and understand or evaluate how a system like a website, an API or even an application will behave under heavy load or real world usage.

For example, when we deploy an API, we are unable to properly estimate how our API will behave when "x" number of users start using it. We need to know this kind of information so as to properly provision resources (move to a server with better specifications) or even know the bottlenecks of our applications so we can work on making them better without degrading the service we are providing the user.

What is Locust?

Locust is an open source load testing tool used to define user behaviour with Python code, and swarm your system with millions of simultaneous users. Locust Documentation

Load Testing - Installation of Locust

You first need to create a directory for the load test and then create and activate your virtual environment. Then you can install locust:

uv add locust || pip install locust

Locustfile Creation

So to run our load tests we need a file with our test instances in it. We can create a locustfile.py in our project directory. We can add the following code to our locust file:

import time
from locust import HttpUser, task, between

class NormalUser(HttpUser):
    wait_time = between(1, 5)
    
    @task
    def get_products(self):
        self.client.get("/products")
    
    @task(3)
    def get_product_details(self):
        product_id = 1
        self.client.get(f"/products/{product_id}")
    
    @task(2)
    def create_order(self):
        self.client.post("/orders", json={
            "product_id": 1,
            "quantity": 2
        })

Understanding the Locustfile

Let me break down what's happening in this code:

HttpUser Class: This is the base class that represents a user interacting with your system. Each simulated user will be an instance of this class.

wait_time: This defines the time each user waits between executing tasks. between(1, 5) means users will wait randomly between 1 and 5 seconds between requests, simulating real user behavior.

@task Decorator: Methods decorated with @task represent actions that users will perform. Locust will randomly pick tasks for each user to execute.

Task Weights: You can pass a number to the @task decorator (like @task(3)) to control how often a task is executed relative to others. Higher numbers mean the task runs more frequently.

self.client: This is an HTTP client that makes requests to your application. It automatically tracks response times and success/failure rates.

Running Your Load Test

Once you've created your locustfile, you can run it with:

locust -f locustfile.py --host=http://localhost:8000

Then open your browser and navigate to http://localhost:8089. You'll see the Locust web interface where you can:

  • Set the number of users to simulate
  • Set the spawn rate (how quickly users are added)
  • Start and stop the test
  • View real-time statistics and charts

Command Line Options

You can also run Locust in headless mode without the web interface:

locust -f locustfile.py --host=http://localhost:8000 --users 100 --spawn-rate 10 --run-time 5m --headless

This command will:

  • Simulate 100 users
  • Spawn 10 users per second
  • Run for 5 minutes
  • Run without the web UI

Advanced Features

Multiple User Types

You can define different user behaviors by creating multiple user classes:

from locust import HttpUser, task, between

class BrowsingUser(HttpUser):
    weight = 3
    wait_time = between(2, 5)
    
    @task
    def browse_products(self):
        self.client.get("/products")

class PurchasingUser(HttpUser):
    weight = 1
    wait_time = between(5, 10)
    
    @task
    def make_purchase(self):
        self.client.post("/checkout", json={"cart_id": 123})

The weight attribute determines the ratio of each user type. Here, 75% will be browsing users and 25% will be purchasing users.

Authentication

For testing authenticated endpoints:

class AuthenticatedUser(HttpUser):
    def on_start(self):
        # This runs once when each user starts
        response = self.client.post("/login", json={
            "username": "test_user",
            "password": "password123"
        })
        self.token = response.json()["token"]
    
    @task
    def access_protected_resource(self):
        self.client.get("/profile", headers={
            "Authorization": f"Bearer {self.token}"
        })

Analyzing Results

Locust provides several metrics to help you understand your system's performance:

  • Request count: Total number of requests made
  • Failure rate: Percentage of failed requests
  • Response times: Min, max, average, and percentiles (50th, 95th, 99th)
  • Requests per second: Throughput of your system
  • Response time charts: Visual representation of performance over time

Best Practices

  1. Start small: Begin with a low number of users and gradually increase to find your system's breaking point
  2. Realistic use cases: Model your tests after actual user behavior patterns
  3. Monitor your system: Use tools like monitoring dashboards alongside Locust to see CPU, memory, and database metrics