Session 4 · Setup and clean-up: on_start, on_stop and test events#
The big idea#
Some work should happen once per user (log in before shopping, log out afterwards) and some once per
test (create test data first, print a summary at the end). Locust gives you hooks for both:
on_start and on_stop on every user, and the test_start and test_stop events for the whole test.
Everyday example: a restaurant’s day. The owner opens the shop and switches on the lights once
(test_start). Each waiter, when their shift starts, puts on an apron (on_start), serves tables all day (tasks), and
hands in the till at the end of the shift (on_stop). At closing time the owner counts the day’s money once (test_stop).
The hooks in action#
import itertools
from locust import User, between, events, task
ids = itertools.count(1)
@events.test_start.add_listener
def on_test_start(environment, **kwargs):
print("TEST START: the whole test begins (runs once)")
@events.test_stop.add_listener
def on_test_stop(environment, **kwargs):
print("TEST STOP: the whole test is over (runs once)")
class Customer(User):
wait_time = between(0.5, 0.8)
def on_start(self):
self.name = f"user-{next(ids)}"
print(f" on_start {self.name}: runs once when this user is created")
@task
def shop(self):
print(f" task {self.name}: shopping")
def on_stop(self):
print(f" on_stop {self.name}: runs once when this user is stopped")
Two users for 3 seconds:
locust -f 04_lifecycle.py --headless -u 2 -r 2 -t 3s --only-summary --loglevel WARNING
TEST START: the whole test begins (runs once)
on_start user-1: runs once when this user is created
task user-1: shopping
on_start user-2: runs once when this user is created
task user-2: shopping
task user-1: shopping
task user-2: shopping
task user-2: shopping
task user-1: shopping
task user-2: shopping
task user-1: shopping
task user-2: shopping
task user-1: shopping
on_stop user-2: runs once when this user is stopped
on_stop user-1: runs once when this user is stopped
TEST STOP: the whole test is over (runs once)
Reading the order:
TEST STARTappears once, before any user exists.Each user prints its
on_startline once when it is created.user-2starts a moment afteruser-1because the spawn rate is limited (-r 2), and the first user may already be running tasks by then.The
tasklines interleave, since the users run side by side.When the time limit hits, every user runs
on_stoponce, thenTEST STOPappears once.
When to use which#
Hook |
Runs |
Typical use |
|---|---|---|
|
Once, before the load starts |
Create test data, record the start time |
|
Once per user |
Log in, fetch a token, pick a product |
|
Once per user |
Log out, delete the user’s temporary data |
|
Once, after the load stops |
Print or save a summary, clean up shared data |
Log-in is the classic case. If every user logged in inside a task, the login would be hit thousands of times;
in on_start it is hit once per user, like a real session (Session 5 does this with HTTP).
Common mistakes#
Putting a login in a normal
@task. It repeats on every loop and floods the login endpoint.Doing slow setup in
on_startfor 1000 users. The setup can become the load. Prepare shared data intest_startinstead.Assuming the order of users. Users start and stop in an unpredictable order; do not depend on
user-1finishing first.Forgetting
on_stopisn’t guaranteed on a crash. If the process is killed, the hook does not run. Do critical clean-up another way, too.Using
__init__for setup. Locust-specific setup belongs inon_start; the constructor is used by Locust itself.
Hands-on exercises#
Exercise 1. Make test_stop print how many users ran, using a counter incremented in on_start.
Solution
started = 0
class Customer(User):
def on_start(self):
global started
started += 1
...
@events.test_stop.add_listener
def on_test_stop(environment, **kwargs):
print(f"{started} users ran")
With -u 2 it prints 2 users ran.
Exercise 2. Which hook would you use to create a product that all users will then browse?
Solution
events.test_start: it runs once, before the users, so the product exists when the first user arrives. Creating
it in on_start would make N copies.
Exercise 3. Why is a “log out” in on_stop better than at the end of a task?
Solution
A task runs again and again, so it would log the user out in the middle of the session. on_stop runs once, when the
user’s life ends.
Practice#
Take the Session 4 quiz. The original pages on on_start/on_stop and on test_start/test_stop are in the
reference section.