Skip to main content

Writing Load Tests

Cicada is a powerful tool for running load tests. It works by spinning up multiple users to run tests against your services in parallel. In this guide, we'll create a simple load test to simulate scaling load against the demo API in the integration testing guide.

Creating the Test

To begin, create a test directory as shown in the integration testing guide. After initializing the project, update the post_user test:

...
from cicadad.core.decorators import scenario, load_model, user_loop
from cicadad.core.scenario import n_seconds, iterations_per_second_limited
...

@scenario(engine)
@load_model(n_seconds(180, 30))
@user_loop(iterations_per_second_limited(4))
def post_user(context):
requests.post(
url="http://localhost:8080/users",
json={
"name": "jeremy",
"age": 23,
"email": f"{str(uuid.uuid4())[:8]}@gmail.com",
},
)

This will make the post_user test spawn 30 users to run for 3 minutes. Each user will be limited to making 4 requests per second.

Start the test with cicada-distributed run and ensure it completes.

Viewing Metrics from the API

The demo API is bundled with Grafana and Prometheus. You can view the metrics by visiting the Grafana UI at http://localhost:3000/ (default username and password is admin / admin).

Create a graph with the Prometheus datasource (http://prometheus:9090) to view the requests per second to the API. Here is the view using the query rate(starlette_requests_total[30s]):

Load Model With 30 Users

Adding Stages to the Test

In a more realistic scenario, load may come in different stages. Let's update the scenario to include a period of scaling up and down. The test will now have 3 stages:

  1. Ramp up to 30 users over the course of 1 minute.
  2. Hover at 30 users for 3 minutes.
  3. Scale back down to 0 users over the course of 1 minute.
...
from cicadad.core.decorators import scenario, load_model, user_loop
from cicadad.core.scenario import (
n_seconds,
iterations_per_second_limited,
n_users_ramping,
load_stages
)
...

@scenario(engine)
@load_model(
load_stages(
n_users_ramping(60, 30, skip_scaledown=True),
n_seconds(180, 30, skip_scaledown=True),
n_users_ramping(60, 0),
)
)
@user_loop(iterations_per_second_limited(4))
def post_user(context):
requests.post(
url="http://localhost:8080/users",
json={
"name": "jeremy",
"age": 23,
"email": f"{str(uuid.uuid4())[:8]}@gmail.com",
},
)

Run the test again and ensure it completes. On the machine this was tested on, it reached 120 requests per second over the course of 5 minutes with a much smoother load curve than the previous test.

Load Model With Ramping Users