Chapter 3 · HttpUser and Task Writing
Chapter goals: Master the
clientattribute usage on HttpUser (GET/POST/parameter passing), learn to aggregate statistics with thenameparameter, and understand theon_startlifecycle hook.
3.1 Core HttpUser Attributes
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")| Attribute | Type | Description |
|---|---|---|
client | HttpSession | Entry point for HTTP requests; API compatible with requests |
host | str | Target host, overridable via CLI -H |
environment | Environment | Current test environment object (access runner, stats, etc.) |
3.2 Issuing Different Request Types
self.client supports all HTTP methods:
@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:
@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:
# ❌ 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.
@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:
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.clientmirrors 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
- Write a task that calls
/api/products/{id}for ids 1–20, usingname="/api/products/[id]"to group all into one stat row. - Add
catch_response=Truevalidation: assert the response JSON contains a"price"field; mark failure if missing. - Add an
on_start()method that logs in via POST/loginand stores the token inself.user_token.
Proceed to Chapter 4: Task Weights and Wait Time.