Test guides
Custom Shapes
Custom Shapes allow you to have very tight programmable control over the shape of user load in your tests. This is an advanced feature, and we recommend the default for most use cases.
For our examples we will be using a very simple test that just gets the index page, but with multiple different custom shape examples.
Advanced Users Only
Default LoadForge tests are usually the best unless you have a very specific use case, such as Custom Shapes. We recommend sticking to the default.
Basic Custom Shape
This shape class will increase user count in blocks of 100 and then stop the load test after 10 minutes:
from locust import HttpUser, TaskSet, task, between
from locust import LoadTestShape
class QuickstartUser(HttpUser):
wait_time = between(5, 9)
@task(1)
def index_page(self):
self.client.get("/")
class MyCustomShape(LoadTestShape):
time_limit = 600
spawn_rate = 20
def tick(self):
run_time = self.get_run_time()
if run_time < self.time_limit:
# User count rounded to nearest hundred.
user_count = round(run_time, -2)
return (user_count, spawn_rate)
return None
Double Wave Custom Shape
This shape class will have two peaks of users, 60 users and then 35 users over a period of 300 seconds (5 minutes).
import math
from locust import HttpUser, TaskSet, task, between
from locust import LoadTestShape
class QuickstartUser(HttpUser):
wait_time = between(5, 9)
@task(1)
def index_page(self):
self.client.get("/")
class DoubleWave(LoadTestShape):
"""
A shape to imitate some specific user behavior. In this example, midday
and evening meal times. First peak of users appear at time_limit/3 and
second peak appears at 2*time_limit/3
Settings:
min_users -- minimum users
peak_one_users -- users in first peak
peak_two_users -- users in second peak
time_limit -- total length of test
"""
min_users = 20
peak_one_users = 60
peak_two_users = 40
time_limit = 300
def tick(self):
run_time = round(self.get_run_time())
if run_time < self.time_limit:
user_count = (
(self.peak_one_users - self.min_users)
* math.e ** -(((run_time / (self.time_limit / 10 * 2 / 3)) - 5) ** 2)
+ (self.peak_two_users - self.min_users)
* math.e ** -(((run_time / (self.time_limit / 10 * 2 / 3)) - 10) ** 2)
+ self.min_users
)
return (round(user_count), round(user_count))
else:
return None
Stages Custom Shape
This custom shape allows you to specify the users that are active at points throughout the test.
from locust import HttpUser, TaskSet, task, between
from locust import LoadTestShape
class QuickstartUser(HttpUser):
wait_time = between(5, 9)
@task(1)
def index_page(self):
self.client.get("/")
class StagesShape(LoadTestShape):
"""
A simply load test shape class that has different user and spawn_rate at
different stages.
Keyword arguments:
stages -- A list of dicts, each representing a stage with the following:
duration -- When this many seconds until advancing to the next stage
users -- Total user count
spawn_rate -- Number of users to start/stop per second
stop -- A boolean that can stop that test at a specific stage
stop_at_end -- Can be set to stop once all stages have run.
"""
stages = [
{"duration": 60, "users": 10, "spawn_rate": 10},
{"duration": 100, "users": 50, "spawn_rate": 10},
{"duration": 180, "users": 100, "spawn_rate": 10},
{"duration": 220, "users": 30, "spawn_rate": 10},
{"duration": 230, "users": 10, "spawn_rate": 10},
{"duration": 240, "users": 1, "spawn_rate": 1},
]
def tick(self):
run_time = self.get_run_time()
for stage in self.stages:
if run_time < stage["duration"]:
tick_data = (stage["users"], stage["spawn_rate"])
return tick_data
return None
User Count Shape
A step load shape that waits until the target user count has been reached before waiting on a per-step timer.
import math
from locust import HttpUser, TaskSet, task, between, constant
from locust import LoadTestShape
class QuickstartUser(HttpUser):
wait_time = between(5, 9)
@task(1)
def index_page(self):
self.client.get("/")
class StepLoadShape(LoadTestShape):
"""
A step load shape that waits until the target user count has
been reached before waiting on a per-step timer.
The purpose here is to ensure that a target number of users is always reached,
regardless of how slow the user spawn rate is. The dwell time is there to
observe the steady state at that number of users.
Keyword arguments:
targets_with_times -- 2-tuples, with the desired user count first,
and the dwell (hold) time with that user count second
"""
targets_with_times = (Step(10, 10), Step(20, 15), Step(10, 10))
def __init__(self, *args, **kwargs):
self.step = 0
self.time_active = False
super(StepLoadShape, self).__init__(*args, **kwargs)
def tick(self):
if self.step >= len(self.targets_with_times):
return None
target = self.targets_with_times[self.step]
users = self.get_current_user_count()
if target.users == users:
if not self.time_active:
self.reset_time()
self.time_active = True
run_time = self.get_run_time()
if run_time > target.dwell:
self.step += 1
self.time_active = False
# Spawn rate is the second value here. It is not relevant because we are
# rate-limited by the User init rate. We set it arbitrarily high, which
# means "spawn as fast as you can"
return (target.users, 100)
Step Load Shape
A step load shape that waits until the target user count has been reached before waiting on a per-step timer.
import math
from locust import HttpUser, TaskSet, task, between, constant
from locust import LoadTestShape
class QuickstartUser(HttpUser):
wait_time = between(5, 9)
@task(1)
def index_page(self):
self.client.get("/")
class StepLoadShape(LoadTestShape):
"""
A step load shape
Keyword arguments:
step_time -- Time between steps
step_load -- User increase amount at each step
spawn_rate -- Users to stop/start per second at every step
time_limit -- Time limit in seconds
"""
step_time = 30
step_load = 10
spawn_rate = 10
time_limit = 600
def tick(self):
run_time = self.get_run_time()
if run_time > self.time_limit:
return None
current_step = math.floor(run_time / self.step_time) + 1
return (current_step * self.step_load, self.spawn_rate)