Load tests (Locust)
Table of contents
Introduction
splent locust starts a Locust load test inside your product’s web container, which already has the framework and every feature installed. splent locust:stop stops it. Both need a selected product (SPLENT_APP) that is running.
splent locust
Command: docker exec -d -e WORKING_DIR=/workspace my_first_app_web sh -c ...
Locust is running at http://localhost:8089
Stop it with: splent locust:stop
Open the URL and start the swarm. Each product publishes Locust on 8089 plus its port offset, like its web and database ports, so several products can run it at once.
Load-testing one feature
splent locust notes
Without an argument it runs every feature’s locustfile. The short name, the package (splent_feature_notes) and the pyproject form (splent-io/splent_feature_notes@v1.2.0) all work.
Stopping it
splent locust:stop
It prints Locust stopped., or No Locust process was running. when there was nothing to stop.
Writing a locustfile
Each feature keeps its load test in tests/load/locustfile.py, outside the default pytest run. feature:create scaffolds one that requests the feature’s own route and counts 200 and 302, the login redirect, as success.
from locust import HttpUser, TaskSet, between, task
from splent_framework.environment.host import get_host_for_locust_testing
class NotesBehavior(TaskSet):
@task
def index(self):
with self.client.get("/notes", allow_redirects=False, catch_response=True) as response:
if response.status_code in (200, 302):
response.success()
else:
response.failure(f"Notes index returned {response.status_code}")
class NotesUser(HttpUser):
tasks = [NotesBehavior]
wait_time = between(5, 9)
host = get_host_for_locust_testing()
- Use
wait_time. Locust 2.x ignoresmin_waitandmax_wait. get_host_for_locust_testing()returnshttp://localhost:5000, the dev server next to Locust. SetLOCUST_TARGET_URLto aim elsewhere.splent_framework.locust.commongives youget_csrf_token(response), which reads a form’s hiddencsrf_token, andfake, a Faker instance.
Advanced
The framework module
splent_framework.bootstraps.locustfile_bootstrapfinds everylocustfile.pyunder the product’sfeatures/folder, following its links, and imports each under its own module name. A locustfile that fails to import is skipped with a logged exception.SPLENT_LOCUSTFILESreplaces that search with comma-separated glob patterns, relative toWORKING_DIRunless absolute.
Reference
| Argument | Description |
|---|---|
FEATURE |
Load-test only this feature |
Before it starts, it runs locust --list in the container, which imports every locustfile. Then it starts Locust detached, logging to /tmp/locust.log, and checks two seconds later that it is still alive.
| Message | What to do |
|---|---|
Container '<product>_web' is not running. |
Start the product with splent product:up |
locust is not installed in '<product>_web'. |
Add "locust" to [project.optional-dependencies].dev and run splent product:derive, or docker exec <product>_web pip install locust once |
No locustfile found for feature '<feature>' |
Put it where the message says it looked, usually tests/load/locustfile.py |
Locust could not load the locustfiles, so it was not started. |
Fix the error in Locust’s output below it |
Locust exited right after it started. |
Read the last lines of the log it prints. A busy port is a common cause |
Locust is already running in the product container. |
Stop it first with splent locust:stop |
... does not publish port 8089. |
Add "<host port>:8089" to the web service’s ports and recreate the container |
All but the last two exit 1. Locust already running starts nothing, and a missing port only warns.