Skip to content

Chapter 3 · HttpUser and Task Writing ​

Chapter goals: Master the client attribute usage on HttpUser (GET/POST/parameter passing), learn to aggregate statistics with the name parameter, and understand the on_start lifecycle hook.

3.1 Core HttpUser Attributes ​

python
from locust import HttpUser, task


class ApiUser(HttpUser):
    host = "https://httpbin.org"  # Default target host

    @task
    def make_request(self):
        # self.client is an HttpSession instance
        resp = self.client.get("/get")
AttributeTypeDescription
clientHttpSessionEntry point for HTTP requests; API compatible with requests
hoststrTarget host, overridable via CLI -H
environmentEnvironmentCurrent test environment object (access runner, stats, etc.)

3.2 Issuing Different Request Types ​

self.client supports all HTTP methods:

python
@task
def http_methods(self):
    # GET with query parameters
    self.client.get("/get", params={"page": 1, "size": 20})

    # POST with JSON body
    self.client.post("/post", json={"username": "locust", "active": True})

    # PUT / DELETE / PATCH work the same way
    self.client.put("/put", json={"id": 1})
    self.client.delete("/delete")
    self.client.head("/get")

With custom headers:

python
@task
def with_headers(self):
    self.client.get(
        "/headers",
        headers={"Authorization": "Bearer my-token", "X-Custom": "value"},
    )

3.3 Aggregating Statistics with name ​

This is the most easily overlooked but most important technique for beginners. Compare:

python
# ❌ Not recommended: each product ID generates a separate stat row
self.client.get(f"/product/{product_id}")

# ✅ Recommended: all product pages grouped into one "/product" stat
self.client.get(f"/product/{product_id}", name="/product")

Without name, stress-testing 100 different products produces 100 stat rows — each with too few samples for meaningful percentile data. Grouping with name makes the statistics useful.

python
@task
def browse_products(self):
    for product_id in range(10):
        self.client.get(
            f"/api/products/{product_id}",
            name="/api/products/[id]",  # Grouped stat name
        )

3.4 Validating Responses ​

Locust only marks non-2xx HTTP status codes as failures by default. Business-level validation requires manual handling:

python
from locust import HttpUser, task


class UserApi(HttpUser):
    host = "https://httpbin.org"

    @task
    def get_user(self):
        with self.client.get("/json", catch_response=True) as resp:
            if resp.status_code != 200:
                resp.failure(f"Status code error: {resp.status_code}")
            elif "slideshow" not in resp.text:
                resp.failure("Response missing expected field")
            else:
                resp.success()

3.5 Chapter Summary ​

  • self.client mirrors the requests API — GET, POST, PUT, DELETE all supported;
  • name= parameter groups dynamic URL paths into one stat row;
  • catch_response=True + resp.failure() / resp.success() for business-level validation;
  • on_start() runs once per virtual user before tasks begin — ideal for login/setup.

🧪 随堂测验

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

1. What is the purpose of the name= parameter in self.client.get()?

2. How do you perform business-level response validation in Locust?

3. What does on_start() do in an HttpUser class?

4. Which attribute gives you access to the test environment (runner, stats, events)?

🛠️ Hands-On Exercises ​

  1. Write a task that calls /api/products/{id} for ids 1–20, using name="/api/products/[id]" to group all into one stat row.
  2. Add catch_response=True validation: assert the response JSON contains a "price" field; mark failure if missing.
  3. Add an on_start() method that logs in via POST /login and stores the token in self.user_token.

Proceed to Chapter 4: Task Weights and Wait Time.