In Gatling, what are the six ordered steps of a check, and which of them does Gatling supply for you when you leave them out?
answer
- A check is an ordered chain
- Six steps, two have defaults
- Implicit find, implicit exists
- find() means find(0), zero-based
- saveAs and name are never implied
basics
~20 sA Gatling check is an ordered chain of six steps: pick a check type, extract an occurrence, transform it, validate it, name it, save it. Only two are supplied when omitted: an implicit find() and an implicit exists().
solid answer
~40 sGatling reads a check as six chained steps: the **check type** (`jsonPath`, `regex`, `status` …), the **extraction** (`find`, `find(n)`, `findAll`, `count`), an optional **transform**, the **validation** (`is`, `not`, `exists`, `in` …), an optional **name**, and an optional **saveAs**. Two of those have defaults. If you write no extraction step, Gatling inserts `find()`, which is identical to `find(0)` — the first occurrence. If you write no validation step, Gatling inserts `exists()`. So `jsonPath("$.orderId")` alone already means “the first `$.orderId` must be present, or the request is KO”. Transform, `name(...)` and `saveAs(...)` have **no** default: leave `saveAs` off and nothing reaches the Session, even though the check still passes or fails.
code
java · 19 linesimport io.gatling.javaapi.core.*;
import io.gatling.javaapi.http.*;
import static io.gatling.javaapi.core.CoreDsl.*;
import static io.gatling.javaapi.http.HttpDsl.*;
public class CheckStepsSimulation extends Simulation {
ScenarioBuilder scn = scenario("Checkout")
.exec(http("Place order")
.post("/orders")
.check(status().is(201))
// these two lines declare exactly the same check
.check(jsonPath("$.orderId"))
.check(jsonPath("$.orderId").find(0).exists()));
{
setUp(scn.injectOpen(atOnceUsers(1)));
}
}go deeper
Be ready to list the six steps in order and to say which two Gatling inserts for you: find() and exists().
Explain that the chain is a Find to Validate to Final type ladder of immutable builders, and that find() resolves to find(0).
Show how an auto-generated failure message such as jsonPath($.orderId).find.exists reveals the steps that were never written.
Own the convention for when a capture-only check should carry optional() rather than inherit the implicit exists().
A Gatling **check** looks like one method call but is really an ordered chain. Gatling's own reference lists the six steps in this order: defining the check type, extracting, transforming, validating, naming, saving. Reading a check left to right is reading that chain, and most surprises come from the two steps Gatling silently inserts when you skip them. ## The six steps 1. **Check type** — what part of the exchange you are looking at. Generic types such as `responseTimeInMillis`, `bodyString`, `bodyBytes`, `bodyLength`, `bodyStream`, `substring`, `regex`, `css`, `xpath`, `jsonPath` and `jmesPath` are statics on **`CoreDsl`**. HTTP adds its own on **`HttpDsl`**: `status`, `header`, `headerRegex`, `currentLocation`. Importing the wrong one is the most common compile failure here. 2. **Extraction** — which occurrence you want: `find()`, `find(n)`, `findAll()`, `findRandom()`, `count()`. 3. **Transformation** — optional: `transform`, `transformWithSession`, `withDefault`, `transformOption`. 4. **Validation** — the condition: `is`, `not`, `isNull`, `notNull`, `exists`, `notExists`, `in`, `optional`, `validate`. 5. **Naming** — optional `name(...)`, a **static String only**, used in the failure message. 6. **Saving** — optional `saveAs(key)`, also a **static String only**, which writes the value into the virtual user's Session. ## What Gatling fills in | step | left out means | |---|---| | extraction | Gatling inserts `find()`, which is **identical to `find(0)`** — the first or only occurrence | | validation | Gatling inserts `exists()` — fails the check if nothing was captured | | transform | nothing happens; there is no default transform | | name | the failure message is auto-generated from the extractor and validator | | saveAs | **nothing is written to the Session**, even on a passing check | So `substring("expected")` is exactly `substring("expected").find().exists()`, and `jsonPath("$.foo")`, `jsonPath("$.foo").find()` and `jsonPath("$.foo").find(0)` are three spellings of one check. `find(n)` is a **zero-based** rank, so `find(1)` is the second occurrence. ## The mechanism differs by SDK, the behaviour does not * In the **Java API** — which Kotlin and the JavaScript/TypeScript SDK also sit on — the default implementations delegate. `Find.Default.saveAs(key)` calls `find().saveAs(key)`, and `Validate.Default.saveAs(key)` calls `exists().saveAs(key)`. The skipped steps are inserted by the class hierarchy itself. * In **Scala**, three implicit conversions in `io.gatling.core.check.CheckSupport` do the same job: one turns a `Find` into a `Validate` by calling `.find`, one turns a `Validate` into a `Final` by calling `.exists`, and one does both. They arrive with `import io.gatling.core.Predef._`; without that import a Scala check that omits a step will not compile. Either way the ladder is `Find` -> `Validate` -> `Final`, and each rung returns a **new immutable builder**. ## Reading the defaults back out of a failure When a check fails and you have not called `name(...)`, Gatling builds the name as `extractorName.arity.validatorName`. A `jsonPath("$.orderId")` that matched nothing therefore fails with a message beginning `jsonPath($.orderId).find.exists` — the error message literally spells out the two steps you did not write. `arity` renders as `find`, `find(2)`, `findAll`, `count` or `findRandom` depending on the extraction step, so the message also tells you which occurrence was searched. ## The two omissions that bite * **`saveAs` is not implied by extracting.** A check that extracts, passes validation and never calls `saveAs` leaves the Session untouched. Capturing a value and asserting on a value are the same chain, separated only by that last call. Both `saveAs(key)` and `name(n)` take a **static String only**, so neither can be computed per virtual user. * **`exists` is implied.** Writing `jsonPath("$.token")` purely to capture a token also asserts it is present, so a response without it marks the request KO. If you want a genuinely optional capture, say so with `optional()` — it never fails, and when nothing matched, the later steps including `saveAs` do not fire, so an existing Session value is neither overwritten nor removed. `withDefault(value)` is the other escape, supplying a fallback so the remaining steps still run. ## The order the steps run in is not the order you wrote the checks in The six steps run in order **within one check**, but the checks on a single response are another matter. Gatling walks the whole list even after one of them fails, reports only the **first** failure, and still applies the `saveAs` of every check that passed — so a KO request can legitimately have written Session attributes. For HTTP it also sorts the checks by scope before running them: URL, then status, then header, then body, then time, regardless of the order they appear in your source. That is why a 500 carrying a JSON error payload reports the status failure rather than the body failure. One more consequence of the same machinery: when more than one check is attached to a response, Gatling allocates a **prepared cache** — but it is keyed by the check's `Preparer` **object**, so only checks handed the same preparer share the work. `regex`, `substring` and `bodyString` sit on one shared string preparer, and every `xpath` check on one shared DOM parse, so those are prepared once. It does **not** help `jsonPath`, `jmesPath` or `css`: their materializers are built per check and allocate a fresh preparer, so two `jsonPath` checks on one response parse the JSON body **twice**. ## Where the check types come from Getting the imports right is part of getting the first step right. In Java the preamble is four lines, two per DSL family, because the package wildcard supplies the **types** and no static import can: ```java import io.gatling.javaapi.core.*; import static io.gatling.javaapi.core.CoreDsl.*; import io.gatling.javaapi.http.*; import static io.gatling.javaapi.http.HttpDsl.*; ``` Kotlin writes the same four without the `static` keyword, which it does not have. Scala replaces all four with `import io.gatling.core.Predef._` and `import io.gatling.http.Predef._` — and those are also what bring the implicit conversions above into scope.
- Does an extraction step on its own put the captured value into the Session?No. Saving is a separate, optional step. A check that extracts and validates but never calls `saveAs(key)` leaves the Session untouched — it only decides whether the request is OK or KO. `saveAs` also takes a static String key, so the name cannot be computed at runtime.
- What is the difference between leaving the validation step off and writing `optional()`?Leaving it off inserts `exists()`, so a missing value fails the check and marks the request KO. `optional()` always succeeds; when nothing matched, the remaining steps do not run, so `saveAs` writes nothing and any value already under that key is neither replaced nor removed.
- Is `find(1)` the first or the second occurrence?The second. `find(n)` takes a zero-based rank, so `find(0)` is the first and is what the parameterless `find()` — and therefore an omitted extraction step — resolves to.
It reads like a sentence with two words you may leave unsaid: skip them and Gatling still hears "the first one" and "it must be there".
saying these in an interview costs you the question
- Thinking extracting a value also stores it in the Session without saveAs
- Believing an omitted validation step means the check can never fail
- Treating find(n) as a one-based occurrence rank
- Assuming transform still runs when the extraction captured nothing