Advanced Patterns
Overview
Advanced patterns handle complex, real-world scenarios:
- Conditional branches - Different flows based on conditions
- Loops - Repeat actions multiple times
- Groups - Organize sub-scenarios
- Async operations - Non-blocking patterns
- Error handling - Gracefully handle failures
- Dynamic data - Change behavior based on responses
Pattern 1: Conditional Branches
Simple If/Then
.exec(
http("Check balance")
.get("/account/balance")
.check(jsonPath("$.amount").saveAs("balance"))
)
.doIf(session -> {
double balance = Double.parseDouble(session.getString("balance"));
return balance < 100; // Condition
})
.then(
http("Request funds")
.post("/request-funds?amount=500")
)
If/Then/Else
.exec(
http("Check membership")
.get("/user/membership")
.check(jsonPath("$.tier").saveAs("tier"))
)
.doIfOrElse(session -> session.getString("tier").equals("premium"))
.then(
http("Premium benefits")
.get("/premium-features")
)
.orElse(
http("Free benefits")
.get("/free-features")
)
Pattern 2: Loops
Repeat Loop
While Loop
.asLongAs(session -> session.getInt("count") < 10)
.on(
exec(session -> {
int count = session.getInt("count");
return session.set("count", count + 1);
})
.exec(http("Request")
.get("/data/" + session.getInt("count"))
)
.pause(1)
)
Foreach Loop
.exec(session -> {
List<String> productIds = List.of("P1", "P2", "P3", "P4", "P5");
return session.set("productIds", productIds);
})
.foreach("#{productIds}", "productId")
.on(
exec(http("View product")
.get("/products/#{productId}")
)
.pause(1)
)
Pattern 3: Groups
Organize Sub-Scenarios
// Browsing group
ScenarioBuilder browsing = scenario("Browsing")
.exec(http("Homepage").get("/"))
.pause(2)
.exec(http("Products").get("/products"))
.pause(1)
.exec(http("Filters").get("/products?category=electronics"))
.pause(1)
.exec(http("Sort").get("/products?sort=price"));
// Purchasing group
ScenarioBuilder purchasing = scenario("Purchasing")
.exec(http("Add to cart").post("/cart"))
.pause(1)
.exec(http("Checkout").post("/checkout"))
.pause(2)
.exec(http("Payment").post("/payment"));
// Complete journey
ScenarioBuilder fullJourney = scenario("Full Journey")
.group(browsing) // First: browse
.pause(5) // Think time
.group(purchasing); // Then: purchase
// Use
setUp(fullJourney.injectOpen(constantUsersPerSec(10).during(60)))
Pattern 4: Error Handling
Try/Catch Pattern
.tryMax(3) // Retry up to 3 times
.on(
exec(http("Fetch data")
.get("/api/data")
.check(status().is(200))
)
)
.exitHereIf(session -> session.getBoolean("_counterexceeded"))
// If retries exceeded, continue without failing
Graceful Degradation
.exec(http("Try Premium API")
.get("/api/v2/data")
.check(status().not(500))
)
.doIfOrElse(session -> session.getString("_status").equals("OK"))
.then(
// Premium API succeeded
exec(session -> session.set("apiVersion", "v2"))
)
.orElse(
// Premium API failed, use fallback
exec(http("Fallback to Standard API")
.get("/api/v1/data")
)
.exec(session -> session.set("apiVersion", "v1"))
)
Pattern 5: Dynamic Request Bodies
Use Session Variables in JSON
.feed(userFeeder) // {name: "Alice", age: 30, city: "NYC"}
.exec(http("Create profile")
.post("/profile")
.body(StringBody(
"{\"name\": \"#{name}\", \"age\": #{age}, \"city\": \"#{city}\"}"
))
)
Extract and Reuse
.exec(http("Get product")
.get("/products/#{productId}")
.check(jsonPath("$.price").saveAs("price"))
.check(jsonPath("$.tax").saveAs("tax"))
)
.pause(1)
.exec(http("Add to cart")
.post("/cart")
.body(StringBody(
"{\"productId\": \"#{productId}\", \"price\": #{price}, \"tax\": #{tax}}"
))
)
Pattern 6: Complex Multi-Step Journeys
E-Commerce Journey
ScenarioBuilder ecommerceJourney = scenario("E-Commerce")
// Step 1: Search
.feed(searchTermFeeder)
.exec(http("Search")
.get("/search?q=#{searchTerm}")
.check(jsonPath("$[0].id").saveAs("productId"))
)
.pause(2)
// Step 2: View product
.exec(http("View product")
.get("/products/#{productId}")
.check(jsonPath("$.price").saveAs("price"))
)
.pause(3)
// Step 3: Conditional - Sometimes add to cart
.doIf(session -> Math.random() > 0.3) // 70% add to cart
.then(
exec(http("Add to cart")
.post("/cart")
.body(StringBody("{\"productId\": \"#{productId}\"}"))
)
.pause(1)
// Step 4: Proceed to checkout
.exec(http("Checkout")
.post("/checkout")
.check(jsonPath("$.orderId").saveAs("orderId"))
)
.pause(2)
// Step 5: Payment
.exec(http("Payment")
.post("/payment")
.body(StringBody("{\"orderId\": \"#{orderId}\", \"amount\": #{price}}"))
)
)
// Step 6: Always view confirmation
.doIfOrElse(session -> session.contains("orderId"))
.then(
exec(http("Order confirmation")
.get("/orders/#{orderId}")
)
)
.orElse(
exec(http("Continue browsing")
.get("/products")
)
);
Pattern 7: State Management
Track User State
.exec(session -> {
// Initialize user state
session.set("userState", "logged_out");
session.set("cartItems", 0);
session.set("totalSpent", 0.0);
return session;
})
// State transitions
.exec(http("Login")
.post("/login")
.check(status().is(200))
)
.exec(session -> session.set("userState", "logged_in"))
.repeat(5).on(
exec(http("Shop")
.get("/products")
)
.exec(session -> {
int items = session.getInt("cartItems");
return session.set("cartItems", items + 1);
})
)
.exec(http("Checkout")
.post("/checkout")
)
.exec(session -> session.set("userState", "checked_out"))
Key Takeaways
- Conditional logic enables branching
- Loops repeat actions efficiently
- Groups organize complex scenarios
- Error handling ensures robustness
- Session state enables complex workflows
- Realistic scenarios require all patterns combined
Navigation
← Previous: Custom Feeders
→ Next: Optimization Tips
↑ Up: Documentation Index