Locust’s core innovation is that it lets you write load tests in Python, a language most developers are already fluent in.
Let’s see it in action. First, install Locust:
pip install locust
Now, create a simple Locust file, locustfile.py:
from locust import HttpUser, task, between
class WebsiteUser(HttpUser):
wait_time = between(1, 5) # Wait 1-5 seconds between tasks
@task
def index(self):
self.client.get("/")
@task
def about(self):
self.client.get("/about/")
Save this file and run Locust from your terminal in the same directory:
locust -f locustfile.py --host=http://localhost:8000
This command starts a Locust web UI on http://localhost:8089. Open that URL in your browser. You’ll see fields to enter the number of users to simulate and the spawn rate (how many users to start per second). Enter, say, 100 users and a spawn rate of 10, then click "Start swarming." Locust will begin sending HTTP requests to http://localhost:8000 based on the tasks defined in your locustfile.py. The web UI will show you real-time metrics like requests per second, response times, and failure rates.
The problem Locust solves is making load testing accessible and maintainable. Instead of learning a proprietary DSL or dealing with complex XML configurations, you leverage Python’s expressiveness. This means you can easily integrate load testing into your development workflow, use existing Python libraries for data generation or assertion, and have your load tests live alongside your application code.
Internally, Locust operates by spawning Python processes that act as "users." Each user instance runs your Locust file, executing the defined tasks. Locust orchestrates these users, collecting statistics from them and reporting them back to the master process (which serves the web UI). This distributed architecture allows you to scale your load generation by running Locust workers on multiple machines.
The HttpUser class is the fundamental building block for testing HTTP-based services. It provides a client attribute, which is an instance of the requests library, enhanced with Locust’s tracking capabilities. When you call self.client.get("/path"), Locust not only makes the HTTP request but also records the request’s success or failure, its duration, and the response status code. The @task decorator marks methods as actions a user can perform. The wait_time attribute controls the pacing between these tasks, ensuring realistic user behavior.
The between(min_seconds, max_seconds) function is a common way to define a random wait time within a specified range. This prevents your simulated users from hammering the system with perfectly synchronized requests, which is rarely representative of real-world traffic. Other wait time strategies exist, like constant(seconds) for a fixed wait or constant_throughput(requests_per_second) for aiming for a specific overall rate.
A crucial, yet often overlooked, aspect of HttpUser is its ability to manage cookies and sessions automatically. When a HttpUser makes a request that sets a cookie (e.g., for authentication), Locust’s underlying requests client will store that cookie and include it in subsequent requests from the same user instance. This means you don’t have to manually manage session tokens or cookies in your Locust file for most common scenarios, simplifying tests that require user login and state persistence.
The --host argument in the locust command is essential. It specifies the base URL of the system under test. If omitted, Locust defaults to http://localhost:8089, which is usually not what you want. Setting this correctly ensures your tests are directed at the intended environment, whether it’s a local development server, a staging instance, or a production deployment.
The next step in mastering Locust involves understanding how to simulate more complex user flows and handle dynamic data.