Next Practical Step
Encapsulate multi-step user workflows inside Transaction Controllers with “Generate parent sample” checked for cleaner dashboard reports.
Master JMeter flow control: If, While, Loop, ForEach, Switch, and Transaction Controllers with Groovy condition syntax and transaction metrics.
Logic Controllers determine the order and conditions under which samplers in a test plan execute. They allow you to build realistic, branching, looping, and aggregated user flows—such as conditional checkouts, polling asynchronous tasks, looping through array responses, and calculating composite business transaction metrics.
This guide provides a practical reference to the most critical logic controllers, condition syntax, and common orchestration patterns.
| Controller | Purpose | Typical Scenario |
|---|---|---|
| Transaction Controller | Aggregates child samplers into one composite transaction metric | Measuring total checkout duration (3 API calls as 1 business transaction) |
| If Controller | Executes child elements only if a condition evaluates to true | Conditional paths (e.g., execute payment only if balance > 0) |
| While Controller | Loops child elements until a condition evaluates to false | Polling an asynchronous order or job status endpoint until STATUS == READY |
| ForEach Controller | Iterates over an array of indexed JMeter variables (item_1, item_2) | Processing every item extracted by a JSON or RegEx extractor |
| Loop Controller | Executes children a fixed number of times | Repeating a specific step within a thread iteration |
| Switch Controller | Switches execution to one child based on a numeric index or name | Multi-branch routing based on user type (ADMIN, CUSTOMER, GUEST) |
| Once Only Controller | Executes only during the first iteration of each thread | Login or token retrieval at thread startup |
| Runtime Controller | Limits execution of child elements to a specified number of seconds | Time-boxing a specific test phase |
The If Controller controls conditional branching.
// Check string equality\${__groovy(vars.get("userType") == "PREMIUM")}
// Check HTTP response code from previous request\${__groovy(vars.get("JMeterThread.last_sample_ok") == "true")}
// Check numeric value\${__groovy(vars.get("cartTotal").toInteger() > 100)}
// Check variable existence / not empty\${__groovy(vars.get("authToken") != null && !vars.get("authToken").isEmpty())}The While Controller loops until its condition evaluates to false.
LAST: Exits when the last sampler fails. If the sampler before the loop failed, the loop is not entered."false" or "true".To poll an endpoint /api/jobs/\${jobId}/status until status is COMPLETED (with a max retry safety counter):
vars.put("jobStatus", "PENDING")vars.put("pollCount", "0")\${__groovy(vars.get("jobStatus") != "COMPLETED" && vars.get("pollCount").toInteger() < 10)}GET /api/jobs/\${jobId}/statusjobStatusint count = vars.get("pollCount").toInteger() + 1vars.put("pollCount", count.toString())When a JSON Extractor extracts an array with match number -1 (all matches), JMeter generates indexed variables:
productId_matchNr = 3productId_1 = prod-101productId_2 = prod-102productId_3 = prod-103productId0 (or 1)\${productId_matchNr}currentProductIdInside the controller, simply reference \${currentProductId} on every iteration.
The Transaction Controller groups multiple HTTP requests into a single parent business transaction (e.g., “Checkout Journey” consisting of /cart/validate, /payment/authorize, and /order/create).
Routes execution to one specific child element based on:
0 for 1st child, 1 for 2nd child.\${paymentType} → routes dynamically to child sampler named CREDIT_CARD or PAYPAL.Enforces a hard time cap on child samplers:
60 (threads run child loop continuously for 60 seconds, then exit).