In Gatling, which check types can return more than one occurrence, and what happens when you call findAll or count on one that cannot?
answer
- Two builder types, not one
- MultipleFind adds find(n), findAll, count
- jsonPath multiple, jmesPath single
- The restriction is a compile error
- substring extracts indices, not text
basics
~20 sMulti-occurrence check types such as regex, css, xpath, jsonPath, substring and header return a MultipleFind builder that offers find(n), findAll, findRandom and count. Single-value types such as status, jmesPath, currentLocation, responseTimeInMillis and bodyString do not, so those calls fail to compile.
solid answer
~40 sGatling splits check builders into two types. A **`MultipleFind`** adds `find(n)`, `findAll()`, `findRandom()` and `count()` on top of the single-value `Find`. `regex`, `css`, `xpath`, `jsonPath`, `substring`, `header` and `headerRegex` are `MultipleFind`; `status`, `currentLocation`, `jmesPath`, `responseTimeInMillis`, `bodyString`, `bodyBytes`, `bodyLength` and `bodyStream` are plain `Find`. The pairing that catches people is **`jsonPath` versus `jmesPath`**: they look interchangeable, but only `jsonPath` can return several values, so `jmesPath("foo").findAll()` does not exist. Because the restriction lives in the **type**, the failure is a compile error, not a run-time surprise. Also note `substring` extracts **indices**, not matched text, so `substring("foo").count()` counts occurrences while `findAll()` yields a list of Ints.
code
java · 21 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 OccurrenceSimulation extends Simulation {
ScenarioBuilder scn = scenario("Orders")
.exec(http("List orders")
.get("/orders")
// jsonPath is a MultipleFind: all of these compile
.check(jsonPath("$..orderId").findAll().saveAs("orderIds"))
.check(jsonPath("$..orderId").count().gt(0))
.check(jsonPath("$..orderId").find(1).saveAs("secondOrderId"))
// jmesPath is a plain Find: only the single-value form exists
.check(jmesPath("total").ofInt().gte(1)));
{
setUp(scn.injectOpen(atOnceUsers(1)));
}
}go deeper
Be ready to name the extraction options: find, find(n), findAll, findRandom and count, and to say that find(n) counts from zero.
Explain that Find and MultipleFind are two builder types, so calling findAll on a single-value check is a compile error rather than a run-time failure.
Show why you would choose jmesPath for a single value and jsonPath where several nodes may match, using the type as the signal.
Own the house guidance on substring versus regex for presence checks, given substring's lower cost and its index-valued result.
The extraction step is the second rung of Gatling's check chain, and which rungs are even offered to you is decided by the **type** the check builder returns — not by the response you happen to get back. ## Two builder types, one hierarchy * **`Find<X>`** — a single value. It offers the parameterless `find()` and goes straight on to validation. * **`MultipleFind<X> extends Find<X>`** — adds `find(int occurrence)`, `findAll()`, `findRandom()`, `findRandom(num)`, `findRandom(num, failIfLess)` and `count()`. Because `MultipleFind` extends `Find`, everything a single-value check can do a multi-value check can do too. The reverse is not true, and that asymmetry is the whole answer. ## Which check type is which | check type | builder | notes | |---|---|---| | `regex`, `css`, `xpath`, `substring` | `MultipleFind` | body extractors, many matches expected | | `jsonPath`, `jsonpJsonPath` | `MultipleFind` | a JSONPath expression can select several nodes | | `jmesPath`, `jsonpJmesPath` | **`Find`** | JMESPath returns exactly one result by design | | `header(name)`, `headerRegex(name, pattern)` | `MultipleFind` | a header may legitimately repeat | | `status`, `currentLocation` | `Find` | one status, one final URL | | `responseTimeInMillis`, `bodyString`, `bodyBytes`, `bodyLength`, `bodyStream` | `Find` | one value per response | | `form(selector)` | `MultipleFind` | captures inputs into a Map | ## What actually happens when you call the wrong one Nothing at run time — the code does not compile. `jmesPath("foo")` hands back a `Find`-shaped builder in the Java API and a `CheckBuilder.Find` in Scala, and neither declares `findAll` or `count`. Gatling's own sample makes the point directly: it notes that `jmesPath("foo")` and `jmesPath("foo").find()` are identical *"because jmesPath can only return 1 value, so find is better omitted"*, while `jsonPath("$.foo")`, `jsonPath("$.foo").find()` and `jsonPath("$.foo").find(0)` are three spellings of the same single-value check on a builder that **could** have returned more. This is the practical reason to prefer `jmesPath` when you want one value and `jsonPath` when you may want several: the type tells you which you have. ## The extraction options, precisely 1. **`find()`** — first or only occurrence; identical to `find(0)`. This is what an omitted extraction step becomes. 2. **`find(n)`** — a **zero-based** rank, so `find(1)` is the second occurrence. 3. **`findAll()`** — every occurrence, as a list. The validation step then compares lists, so `is(...)` takes a list. 4. **`findRandom()`** — one at random; `findRandom(num)` takes several, and an optional `failIfLess` boolean (default `false`) decides whether taking fewer than asked is a failure. 5. **`count()`** — the number of occurrences, as an Int. The validation step then compares numbers: `regex("https://(.*)").count().is(5)`. ## `substring` extracts indices, not text `substring(pattern)` is a `MultipleFind` whose extracted type is **Int** — it reports the *positions* of the occurrences in the body, not the matched strings. That is why Gatling's own sample comments `substring("foo").findAll().saveAs("indices")` as saving a list of Ints. It is a cheap presence test, deliberately cheaper than a regular expression, and it is not a way to pull text out of a body. Use `regex` or `jsonPath` for that. ## How the extraction step shows up in a failure Gatling names an unnamed check `extractorName.arity.validatorName`, where the **arity** is exactly the extraction step you chose. It renders as `find`, `find(2)`, `findAll`, `count`, `findRandom` or `findRandom(3, true)`. Note that `find(0)` renders as plain `find`, so an explicit first-occurrence extraction is indistinguishable in the message from an omitted one. ## Typed extraction is a separate step Asking for several occurrences is not the same as asking for a different *type*. Changing the extracted type is its own step — `ofInt()`, `ofNode()` and friends in the Java-family SDKs, `ofType[T]` in Scala — and it composes with the extraction step rather than replacing it.
- Why does `jmesPath("foo").findAll()` not compile while `jsonPath("$.foo").findAll()` does?`jsonPath` returns a `MultipleFind` builder, which declares `findAll`, `find(n)`, `findRandom` and `count`. `jmesPath` returns a plain `Find`, because a JMESPath expression yields exactly one result. The restriction is in the returned type, so the compiler rejects it before the run starts.
- What does `findRandom(3, true)` do differently from `findRandom(3)`?Both pick up to three occurrences at random. The optional second argument is `failIfLess`, which defaults to `false`: left off, the check succeeds with however many it found; set to `true`, the check fails when fewer than three matched.
- If `findAll()` is used, what does the validation step compare?A list. The extracted type becomes a list of the check's element type, so `is(...)` expects a list and `saveAs` stores one. Gatling's own sample asserts `regex("https://(.*)/.*").findAll().is(List.of("www.google.com", "gatling.io"))`.
saying these in an interview costs you the question
- Treating jsonPath and jmesPath as interchangeable in every position
- Expecting findAll on status or responseTimeInMillis to compile
- Thinking substring extracts the matched text rather than indices
- Reading find(1) as the first occurrence instead of the second