Custom Shapes allow you to precisely control how users ramp up and down during your tests. This is useful for advanced scenarios where a fixed or linear user load is insufficient.
Tip: The default LoadForge test settings work well for most use cases. Custom Shapes should be used when you have a specific load profile requirement.
Basic Custom Shape
This shape gradually increases users in blocks of 100 over a 10-minute test duration:
from locust import HttpUser, 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 = round(run_time, -2) # Round to nearest 100
return (user_count, self.spawn_rate)
return None
Double Wave Custom Shape
Simulates two peaks of user traffic, useful for scenarios like meal times or daily traffic fluctuations.
import math
from locust import HttpUser, 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):
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.exp(-(((run_time / (self.time_limit / 10 * 2 / 3)) - 5) ** 2))
+ (self.peak_two_users - self.min_users) * math.exp(-(((run_time / (self.time_limit / 10 * 2 / 3)) - 10) ** 2))
+ self.min_users
)
return (round(user_count), round(user_count))
return None
Stages Custom Shape
Define exact user counts at specific times.
from locust import HttpUser, 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):
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": 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"]:
return (stage["users"], stage["spawn_rate"])
return None
Step Load Shape
Increases users in steps and waits at each step before increasing further.
import math
from locust import HttpUser, task, between
from locust import LoadTestShape
class QuickstartUser(HttpUser):
wait_time = between(5, 9)
@task(1)
def index_page(self):
self.client.get("/")
class StepLoadShape(LoadTestShape):
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)
When to Use Custom Shapes
Consider using a custom shape if:
- You need gradual user ramp-ups rather than sudden traffic spikes.
- You are simulating real-world traffic patterns, such as lunch-hour surges.
- Your application has specific scalability concerns requiring controlled increases.
Otherwise, the default LoadForge load shape is recommended for general testing.
By implementing Custom Shapes, you can fine-tune your LoadForge tests to reflect realistic user behavior and performance bottlenecks more accurately.