skip to content

In Gatling, how do you assemble a browse-then-checkout scenario out of two reusable chain values?

level: juniorimportance: must knowfreq 72%

answer

  1. Flows are values, not statements
  2. Two builder types, one shared vocabulary
  3. scenario names it, exec composes it
  4. Chains attach sequentially in the order given

basics

~20 s

Store each flow in a chain value returned by a standalone exec call, then hand both to scenario("Shopper").exec(browse, checkout). Chains are ordinary values, so one scenario composes as many of them as you like, in order.

solid answer

~40 s

Gatling gives you two builder types. `scenario("Shopper")` returns a `ScenarioBuilder` — a named chain that can be injected with a population. A bare `exec(...)` returns a `ChainBuilder` — a detached fragment with no name, which you can store in a constant, pass around and reuse. Both expose the same DSL methods, so you build `browse` and `checkout` as `ChainBuilder` values and then write `scenario("Shopper").exec(browse, checkout)`; the chains are attached sequentially in the order given. Nothing executes where you write it — these calls only build definitions, and only the tree that reaches `setUp` ever runs. The Java, Kotlin, JavaScript and Scala spellings of `scenario` and `exec` are identical.

code

java · 20 lines
java
import io.gatling.javaapi.core.*;

import static io.gatling.javaapi.core.CoreDsl.*;
import static io.gatling.javaapi.http.HttpDsl.*;

public class BrowseThenCheckoutSimulation extends Simulation {

  private static final ChainBuilder BROWSE =
      exec(http("Home").get("/"))
          .exec(http("Category").get("/books"));

  private static final ChainBuilder CHECKOUT =
      exec(http("Cart").get("/cart"))
          .exec(http("Place order").post("/orders"));

  {
    ScenarioBuilder shopper = scenario("Shopper").exec(BROWSE, CHECKOUT);
    setUp(shopper.injectOpen(atOnceUsers(1)));
  }
}

go deeper

for a junior

Be ready to name the two builder types and show, in code, a scenario built from two stored chains.

for a middle

Be ready to explain that both types share one builder API, and that a bare exec call produces a detached, reusable fragment rather than an action.

for a senior

Be ready to argue for naming flows as chain constants in a simulation others will maintain, and to spot dangling definitions that never reach setUp.

for a principal

Be ready to set the convention for how flows are named and shared across a team's simulations, and to say what that convention buys and costs.

Gatling simulations are not scripts that run top to bottom. A simulation is **assembled** from builder values, and only the tree of builders that ultimately reaches `setUp` is executed by the engine. Knowing which value each DSL call hands back is most of what scenario authoring is. ## Two builder types, one shared API | call | returns | can it be injected? | |---|---|---| | `scenario("Shopper")` | a `ScenarioBuilder` — a **named** chain of actions | yes | | `exec(http("Home").get("/"))` | a `ChainBuilder` — a **detached**, unnamed fragment | no | | `scenario("Shopper").exec(...)` | a new `ScenarioBuilder` | yes | | `browse.exec(...)` | a new `ChainBuilder` | no | Both types extend the same internal `StructureBuilder`, which is why they expose exactly the same vocabulary — `exec`, `group`, the loop constructs, the conditional constructs. The only substantive differences are that a `ScenarioBuilder` carries a name and offers the entry point that turns it into a population, while a `ChainBuilder` is a fragment you keep in a variable. That symmetry is the point. A chain is a **value**, not a statement. You can declare it as a constant, return it from a method, put it in a list, and reuse the same instance from several scenarios in the same simulation. ## Building browse-then-checkout ```java public class BrowseThenCheckoutSimulation extends Simulation { private static final ChainBuilder BROWSE = exec(http("Home").get("/")) .exec(http("Category").get("/books")); private static final ChainBuilder CHECKOUT = exec(http("Cart").get("/cart")) .exec(http("Place order").post("/orders")); { setUp(scenario("Shopper").exec(BROWSE, CHECKOUT).injectOpen(atOnceUsers(1))); } } ``` Three things are worth noticing: 1. `BROWSE` and `CHECKOUT` are plain constants. They are built once, when the class is initialised, and nothing about them is specific to the scenario that later uses them. 2. `exec(BROWSE, CHECKOUT)` attaches the two chains **sequentially** — a virtual user runs everything in `BROWSE`, then everything in `CHECKOUT`. 3. No HTTP traffic happens where those lines are written. `http("Home").get("/")` is a definition; if you build one and never attach it to anything that reaches `setUp`, it is a dangling definition with no effect at all. ## The shapes of `exec` In the Java API, `exec` comes in three forms, and the same three exist on `ScenarioBuilder` and on `ChainBuilder`: - `exec(Function<Session, Session>)` — attach a step that manipulates the virtual user's session. - `exec(head, tail...)` — attach one or more executables. Both `ChainBuilder` and every action builder (an HTTP request builder, for example) implement the same `Executable` interface, so you can freely mix them: `exec(BROWSE, http("Ping").get("/ping"), CHECKOUT)`. - `exec(List<ChainBuilder>)` — attach a whole list at once, which is handy when the set of chains is computed rather than written out. There is also a bare, static `exec(...)` — the one that bootstraps a fresh `ChainBuilder` from nothing. That static form is what you call when you write `ChainBuilder browse = exec(...)`, and the instance form is what you call when you write `browse.exec(...)`. ## The same shape in the other SDKs The `scenario` and `exec` spellings are identical in Java, Kotlin, JavaScript, TypeScript and Scala; only the surrounding syntax differs (`val` versus `ChainBuilder`, semicolons or not). Scala uses the same names: ```scala private val browse = exec(http("Home").get("/")).exec(http("Category").get("/books")) private val checkout = exec(http("Cart").get("/cart")).exec(http("Place order").post("/orders")) private val shopper = scenario("Shopper").exec(browse, checkout) ``` The SDKs do diverge at the point where a scenario becomes a population — Scala has a single `inject`, while the Java API (which Kotlin and the JavaScript/TypeScript package sit on) has `injectOpen` and `injectClosed` — but that is registration, not composition. ## What the scenario name is for The string you pass to `scenario` identifies the population in the run's output. Gatling normalises it on the way in: carriage returns, newlines and tabs are each replaced by a space and the result is trimmed, so a name containing whitespace oddities is silently rewritten rather than rejected. ## One chain, several scenarios Because a chain is an immutable value, the same `BROWSE` constant can be attached to a shopper scenario, an admin scenario and a smoke scenario in the same simulation, and none of them affects the others. That is what makes a small library of named flows worth having: each flow is written once and reviewed once, and the scenarios that use it read as a list of business steps rather than as a wall of requests. It also means a chain can be produced by a method. A helper with the signature `static ChainBuilder search(String term)` is a perfectly ordinary way to parameterise a flow, because there is nothing stateful about the value it hands back — the helper builds a fresh chain per call and the caller owns it. ## Common mistakes - Writing `exec(...)` on its own line and expecting a request to be sent there. - Assuming a chain belongs to the first scenario that used it and cannot be shared. - Trying to inject a `ChainBuilder`: only a named `ScenarioBuilder` becomes a population. - Inlining every action into one enormous `scenario(...)` call instead of naming the flows — which costs readability long before it costs anything else.

  • In Gatling, what happens to a tab or a newline inside a scenario name?
    It is normalised, not rejected. `scenario(name)` replaces every carriage return, newline and tab with a single space and trims the result, so the name that reaches the output is a cleaned-up version of what you typed. Gatling's own scenario reference says a tab is forbidden; the implementation actually rewrites it.
  • In Gatling's Java DSL, can you attach a computed list of chains instead of naming each one?
    Yes. `exec` has an overload taking a `List<ChainBuilder>`, and the varargs overload accepts anything implementing `Executable` — which covers both chains and individual action builders. In every form the pieces are attached one after another, in list order.

A chain is a paragraph you keep in a folder; a scenario is the document you paste paragraphs into. Copying a paragraph into a second document does not remove it from the folder.

saying these in an interview costs you the question

  • Believing exec sends the request where the line is written
  • Assuming a chain belongs to only one scenario
  • Trying to inject a detached chain instead of a scenario
  • Thinking scenario() must list every action inline