The Gatling DSL (Domain-Specific Language) is what makes Gatling tests readable. It is a fluent API that reads almost like English. "scenario Browse Products, exec http Home Page get slash, pause 2 seconds." Let me walk you through the full DSL vocabulary -- requests, pauses, conditions, loops, and session manipulation.
// GET request
http("Get Products").get("/api/products")
// GET with query parameters
http("Search Products")
.get("/api/products")
.queryParam("category", "electronics")
.queryParam("page", "1")
.queryParam("sort", "price_asc")
// POST with JSON body
http("Create Order")
.post("/api/orders")
.body(StringBody("{\"productId\": 42, \"quantity\": 2}"))
.asJson()
// POST with form data
http("Login")
.post("/api/login")
.formParam("email", "user@example.com")
.formParam("password", "secret123")
// PUT
http("Update Profile")
.put("/api/profile")
.body(StringBody("{\"name\": \"Updated Name\"}"))
.asJson()
// DELETE
http("Remove Item")
.delete("/api/cart/items/#{itemId}")
// PATCH
http("Partial Update")
.patch("/api/orders/#{orderId}")
.body(StringBody("{\"status\": \"shipped\"}"))
.asJson()Real users do not fire requests back-to-back. They read pages, fill forms, and think. Pauses simulate this think time. Without pauses, your test generates unrealistically high throughput and does not represent real user behavior.
// Fixed pause
.pause(Duration.ofSeconds(3))
// Random pause between min and max (realistic)
.pause(Duration.ofSeconds(2), Duration.ofSeconds(5))
// Pause in milliseconds for fine-grained control
.pause(Duration.ofMillis(500), Duration.ofMillis(1500))
// No pause -- fire immediately
// Just chain the next .exec() directlyScenarioBuilder conditionalFlow = scenario("Conditional Flow")
.exec(
http("Home").get("/")
)
.pause(1)
// doIf -- execute block only if condition is true
.doIf(session -> session.getString("userType").equals("premium")).then(
exec(
http("Premium Dashboard").get("/premium/dashboard")
)
)
// doIfOrElse -- if/else branching
.doIfOrElse(session -> session.getInt("cartSize") > 0).then(
exec(
http("View Cart").get("/cart")
)
).orElse(
exec(
http("Browse Products").get("/products")
)
);// Repeat a fixed number of times
.repeat(5).on(
exec(
http("Browse Page #{counterName}").get("/products?page=#{counterName}")
).pause(1)
)
// Repeat with a counter variable name
.repeat(5, "pageNum").on(
exec(
http("Page #{pageNum}").get("/products?page=#{pageNum}")
).pause(1)
)
// forEach -- iterate over a list stored in session
.foreach("#{productIds}", "productId").on(
exec(
http("Product #{productId}").get("/products/#{productId}")
).pause(Duration.ofMillis(500))
)
// during -- repeat for a duration
.during(Duration.ofSeconds(60)).on(
exec(
http("Polling").get("/api/status")
).pause(5)
)
// asLongAs -- repeat while condition is true
.asLongAs(session -> session.getBoolean("hasNextPage")).on(
exec(
http("Next Page").get("/products?page=#{currentPage}")
).pause(1)
)The session is Gatling's equivalent of JMeter variables. Every virtual user has a session -- a map of key-value pairs. You can save values from responses (like auth tokens) and use them in later requests. Gatling uses the #{variableName} syntax in strings, similar to JMeter's ${variableName}.
ScenarioBuilder loginAndBrowse = scenario("Login and Browse")
// Set a session variable manually
.exec(session -> session.set("userType", "premium"))
// Login and save the auth token from response
.exec(
http("Login")
.post("/api/login")
.body(StringBody("{\"email\":\"user@test.com\",\"password\":\"pass123\"}"))
.asJson()
.check(
status().is(200),
jsonPath("$.token").saveAs("authToken"),
jsonPath("$.userId").saveAs("userId")
)
)
.pause(1)
// Use saved variables in next request
.exec(
http("My Profile")
.get("/api/users/#{userId}")
.header("Authorization", "Bearer #{authToken}")
.check(status().is(200))
)
// Debug -- print session contents to console
.exec(session -> {
System.out.println("Auth token: " + session.getString("authToken"));
return session;
});Session variables are per virtual user, not global. If user A saves an authToken, user B cannot see it. This is by design -- each virtual user has its own isolated session, just like real users have separate browser sessions.
A powerful Gatling pattern is extracting common flows into reusable chain builders. Instead of repeating login steps in every scenario, define login once and compose it into multiple scenarios.
// Reusable login chain
ChainBuilder login = exec(
http("Login")
.post("/api/login")
.body(StringBody("{\"email\":\"#{email}\",\"password\":\"#{password\"}"))
.asJson()
.check(jsonPath("$.token").saveAs("authToken"))
);
// Reusable browse chain
ChainBuilder browse = exec(
http("Products").get("/api/products")
.header("Authorization", "Bearer #{authToken}")
).pause(2).exec(
http("Product Detail").get("/api/products/1")
.header("Authorization", "Bearer #{authToken}")
);
// Reusable checkout chain
ChainBuilder checkout = exec(
http("Add to Cart").post("/api/cart")
.header("Authorization", "Bearer #{authToken}")
.body(StringBody("{\"productId\": 1, \"qty\": 1}"))
.asJson()
).pause(1).exec(
http("Place Order").post("/api/orders")
.header("Authorization", "Bearer #{authToken}")
.body(StringBody("{\"paymentMethod\": \"card\"}"))
.asJson()
);
// Compose scenarios from reusable chains
ScenarioBuilder browserUser = scenario("Browser User")
.exec(login, browse);
ScenarioBuilder buyerUser = scenario("Buyer User")
.exec(login, browse, checkout);Q: How would you handle correlated data between requests in Gatling? For example, logging in and using the auth token in subsequent requests.
A: Gatling uses session variables for correlation. In the login response, I use check() with jsonPath() or regex() to extract the auth token and saveAs() to store it in the session -- for example, check(jsonPath("$.token").saveAs("authToken")). In subsequent requests, I reference it with the #{authToken} expression language syntax in headers or body strings. Each virtual user has its own isolated session, so tokens do not cross between users. This is exactly like JMeter's Regular Expression Extractor plus variable references, but with cleaner syntax. I also use saveAs for any dynamic values like order IDs, session cookies, or CSRF tokens that need to flow between requests.
Key Point: The Gatling DSL covers HTTP methods, pauses, conditions, loops, and session variables. Extract common flows into reusable ChainBuilder methods for clean, maintainable tests.
Key Point: Master the DSL: HTTP methods, pauses, conditions, loops, session variables, and reusable chain builders