Skip to content

Chapter 16 · Plugins and Extensions ​

Chapter goals: Master the locust-plugins third-party library, custom LoadShape load profiles, stepped-pressure patterns, and how to extend Locust's Web UI.

locust-plugins is a third-party extension library involving Locust core maintainers (current version 5.x), providing a range of out-of-the-box enhancements:

bash
pip install locust-plugins
ModuleFunction
locust_plugins.usersPlaywrightUser, SeleniumUser, KafkaUser, FtpUser, etc.
locust_plugins.datasetsData pools (CSV/JSON-driven)
locust_plugins.monitorsMonitoring integration
locust_plugins.listenersTimescale/Grafana reporting

Using PlaywrightUser for Browser-Level Load Testing ​

python
# locustfile.py
from locust_plugins.users import PlaywrightUser


class BrowserUser(PlaywrightUser):
    tasks = [
        {
            "weight": 1,
            "task": lambda page: page.goto("https://example.com"),
            "name": "Visit homepage"
        },
        {
            "weight": 2,
            "task": lambda page: (
                page.goto("https://example.com/login"),
                page.fill("#username", "test"),
                page.fill("#password", "pass"),
                page.click("button[type=submit]")
            ),
            "name": "Login flow"
        }
    ]

Why use PlaywrightUser

Traditional HttpUser only loads-tests the HTTP layer, missing JavaScript rendering time and frontend performance bottlenecks. PlaywrightUser launches a real browser to execute page operations — ideal for evaluating end-to-end user experience.

Using Data Pools for Parametrization ​

python
from locust_plugins.datasets import load_csv_data


class ApiUser(HttpUser):
    # Load test data from CSV, loop-reuse
    dataset = load_csv_data("test_users.csv")

    @task
    def login(self):
        credentials = next(self.dataset)
        self.client.post("/api/login", json={
            "username": credentials["username"],
            "password": credentials["password"]
        })

16.2 Custom Load Shape: LoadShape ​

By default, Locust sets a fixed concurrent user count via the Web UI or CLI. But real load-test scenarios often need dynamic load curves — stepped pressure, peak pulses, or day-night simulation.

Locust provides the LoadTestShape abstract base class; override tick() to control the user count at each moment:

python
from locust import HttpUser, task, between, LoadTestShape


class StagedShape(LoadTestShape):
    """Stepped pressure: 25 → 50 → 100 → 200 users"""
    stages = [
        {"duration": 60,  "users": 25,  "spawn_rate": 5},
        {"duration": 120, "users": 50,  "spawn_rate": 10},
        {"duration": 180, "users": 100, "spawn_rate": 15},
        {"duration": 240, "users": 200, "spawn_rate": 20},
    ]

    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  # End the test

Attach to your user class:

python
class MyUser(HttpUser):
    host = "https://example.com"
    load_shape = StagedShape  # Attach the shape class

    @task
    def get_page(self):
        self.client.get("/")

16.3 Common Load Shapes ​

Ramp-up then hold ​

python
class RampHoldShape(LoadTestShape):
    def tick(self):
        run_time = self.get_run_time()
        if run_time < 60:
            # Ramp: 0 → 100 users in 60 seconds
            return (int(run_time * 100 / 60), 100 / 60)
        elif run_time < 300:
            # Hold: 100 users for 4 minutes
            return (100, 0)
        return None  # End

Peak pulse ​

python
class PulseShape(LoadTestShape):
    def tick(self):
        run_time = self.get_run_time()
        cycle = run_time % 120  # 2-minute cycle
        if cycle < 60:
            return (100 * cycle / 60, 100 / 60)   # Ramp up
        else:
            return (100 * (120 - cycle) / 60, -100 / 60)  # Ramp down

16.4 Chapter Summary ​

  • locust-plugins extends Locust with PlaywrightUser, data pools, and monitoring;
  • LoadTestShape lets you define arbitrary load curves via the tick() method;
  • Common shapes: stepped, ramp-hold, pulse — all expressed by returning (users, spawn_rate) or None to end;
  • Attach a custom shape with load_shape = MyShape on the User class.

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. What does locust-plugins' PlaywrightUser enable?

2. What must the tick() method of LoadTestShape return?

3. How do you attach a custom LoadShape to a User class?

4. What is the difference between load_plugins datasets and manual CSV loading?

🛠️ Hands-On Exercises ​

  1. Install locust-plugins; write a PlaywrightUser that loads a page and asserts the title contains "Example".
  2. Implement a RampHoldShape (ramp to 100 users in 30s, hold for 2min, then end); run and verify the user count curve.
  3. Create a PulseShape that cycles 50→100→50 users every 60 seconds for 3 minutes; observe the RPS chart.

Proceed to Chapter 17: Performance Tuning and RPS Optimization.