When you’re simulating user behavior with Locust, blindly hitting endpoints as fast as possible doesn’t reflect reality. Users pause, they think, they navigate. Locust’s wait_time is your tool to inject this crucial realism.
Let’s see it in action. Imagine a simple Locust file:
from locust import HttpUser, task, between
class WebsiteUser(HttpUser):
wait_time = between(1, 5) # Users wait between 1 and 5 seconds
@task
def index(self):
self.client.get("/")
@task
def about(self):
self.client.get("/about/")
When this user runs, after completing the / request, it won’t immediately start the /about/ request. Instead, it will pause for a random duration between 1 and 5 seconds. Then, after completing /about/, it will pause again before deciding which task to run next (either / or /about/ again, based on their defined weights). This mimics a user browsing your site, taking their time between actions.
The wait_time parameter in your HttpUser class controls this. It accepts a callable that returns the wait time in seconds. Locust provides a few built-in options, but you can define your own complex logic.
The most common way to use wait_time is with between(min_wait, max_wait). This distributes the wait times uniformly between min_wait and max_wait seconds. For example, between(2, 10) means each user will wait between 2 and 10 seconds after each task.
Another option is constant(wait_time). This makes every wait time exactly wait_time seconds. While simpler, it’s less realistic as it removes natural variation. Use constant(3) if you want every pause to be precisely 3 seconds.
For more advanced scenarios, you can define a custom function. This function should accept environment and user as arguments and return the wait time in seconds. For instance, you might want to wait longer after a specific type of request or based on the user’s current state.
from locust import HttpUser, task, between
import random
class AdvancedUser(HttpUser):
# Custom wait time logic: longer waits after visiting /products/
def custom_wait(environment, user):
if environment.runner.stats.entries.get("/products/"):
return random.uniform(5, 15) # Wait 5-15 seconds after a product page visit
else:
return random.uniform(1, 5) # Otherwise, wait 1-5 seconds
wait_time = custom_wait
@task
def index(self):
self.client.get("/")
@task
def products(self):
self.client.get("/products/")
In this AdvancedUser example, if the user has previously visited /products/, their next wait time will be between 5 and 15 seconds. Otherwise, they’ll stick to the default 1 to 5 second wait. This allows for nuanced simulation where certain user actions naturally lead to longer pauses.
You can also use constant_throughput(num_requests_per_second). This aims to achieve a specific requests-per-second rate across all users. Locust calculates the necessary wait time internally to maintain this rate. For example, constant_throughput(10) will try to ensure the system handles 10 requests per second in total. This is useful when you want to stress a system at a particular overall throughput rather than focusing on individual user pacing.
The wait_time is executed after a task completes and before the next task is chosen. This means the time spent processing the request itself is not included in the wait_time. It’s purely the idle time between user actions.
The most common pitfall is forgetting to set wait_time at all. If you don’t define it, Locust defaults to a wait time of 1 second. While better than no wait, it’s often not realistic enough.
If your load tests are showing a significantly higher request rate than you expect in production, and you haven’t explicitly set a wait_time, the default 1-second wait is likely the culprit. You’ll need to increase this to better reflect actual user behavior.
When you’re debugging why your load test results don’t match production metrics, always check the wait_time configuration first. It’s the simplest lever to pull for making your simulation more lifelike.
The next step after mastering realistic wait times is understanding how to distribute your user traffic across different user types using weight.