skip to content

In a Karate feature file, `match items == '#[_ < 5]'` and `match items == '#[]? _ < 5'` both use `_`. What does each one assert?

level: middleimportance: should knowfreq 33%

answer

  1. The same name means two different things
  2. Position relative to the bracket decides
  3. One slot is size, the other members
  4. A question mark after the bracket changes it

basics

~20 s

Inside the brackets, _ is the array's length, so '#[_ < 5]' asserts fewer than five elements. After the brackets, _ is each element, so '#[]? _ < 5' asserts every element is under five.

solid answer

~40 s

Karate's array shortcut has two slots and `_` means something different in each. Whatever sits **inside** the square brackets is evaluated with `_` bound to the array's **size**, so `'#[_ < 5]'` asserts the array holds fewer than five elements and fails with `actual array length is 7`. Whatever follows the closing bracket is applied to **every element**, and a `?` there makes it a per-element predicate, so `'#[]? _ < 5'` re-binds `_` to each element and asserts every one of them is under five. The empty brackets in the second form are the length slot left blank, which skips the length check. Both slots can be filled at once: `'#[_ > 0]? _ < 5'` means non-empty and all under five.

code

gherkin · 10 lines
gherkin
* def foo = ['bar', 'baz']

# the array has fewer than five elements
* match foo == '#[_ < 5]'

# every element is three characters long
* match foo == '#[]? _.length == 3'

# both at once: non-empty, and every element three characters long
* match foo == '#[_ > 0]? _.length == 3'

go deeper

for a junior

Learn to read the shape before memorising the rules: a hash, optional brackets, then an optional expectation for the members. Knowing that brackets mean the array itself is enough at this level.

for a middle

Explain that the underscore is rebound between the two slots, that a bracket body without a standalone underscore is rewritten into a comparison against the length, and which failure message each slot produces.

for a senior

Use the two slots deliberately. A non-empty check plus a uniform-member check in one marker catches the empty-array false pass that a member rule alone will happily let through.

for a principal

Weigh expressiveness against readability across a suite. Markers that pack cardinality and member rules into one string keep failure reports precise, but a codebase where every array assertion is a dense one-liner is one nobody reviews carefully.

## Two slots, one name Karate's array shortcut is a single marker string with two places an expression can go, and the same `_` means different things in each: ``` # [ length expression ] per-element expectation ``` - **Inside** the brackets, `_` is the **size of the array**. - **After** the brackets, a `?` starts a **per-element predicate**, and `_` is the **element**. That is the whole trick, and it is the single most common source of confusion in this corner of the DSL, because the two forms are one character apart. | Expected value | `_` is | Asserts | |---|---|---| | `'#[_ < 5]'` | the array's length | fewer than five elements | | `'#[]? _ < 5'` | each element | every element is under five | | `'#[_ > 0]? _ < 5'` | length, then element | non-empty, and every element under five | ## Inside the brackets: the length Whatever sits between `[` and `]` is evaluated as an expression with `_` bound to the element count, and the node fails when the result is not true, reporting `actual array length is 7`. That makes the bracket slot a general length rule rather than a fixed count: ```gherkin * def items = [1, 2, 3] * match items == '#[_ < 5]' * match items == '#[_ % 2 == 1]' ``` The engine decides between "expression" and "plain value" by looking for a **standalone** `_` in the body — an underscore that stands next to a non-word character. If it finds one, the body is used as written. If it does not, the body is rewritten as `<body> == _` before evaluation and therefore compared to the length. That rewrite is why a bare count behaves the way it does, and why a variable whose *name* merely contains an underscore, like `max_size`, is still treated as a value and not as the placeholder. ## After the brackets: the element Anything following the closing bracket is applied to every element. When it begins with a `?`, Karate turns it into an ordinary self-validation marker and runs it against each element in turn — the same machinery as `match each`. So `_` there is the element, and the predicate is evaluated once per item: ```gherkin * def foo = ['bar', 'baz'] * match foo == '#[]? _.length == 3' ``` The empty brackets are not decoration: they are the length slot left blank, which skips the length check. Filling both slots in one marker is legal and often what you actually mean, as in `'#[_ > 0]? _.length == 3'`. ## Reading a failure The two slots fail differently, which is the fastest way to tell which one you tripped: 1. A length rule that comes out false reports `actual array length is N` against the array node. 2. A per-element predicate that comes out false reports `evaluated to 'false'` against the element's own JSON path, so the report names the index. 3. Either form, given a value that is not a list at all, fails first with `actual is not an array` — that check runs before anything inside the brackets is evaluated. ## Choosing a form - Use the bracket slot for **cardinality**: emptiness, a bound, a count cross-checked against another field. - Use the trailing predicate for a rule about **every member**: a range, a length, a membership test, a call into a `def`-ed validator function. - Use both when the array must be non-empty *and* uniform, which is the common real expectation and reads better as one marker than as two steps. ## Why it is one marker and not two steps The trailing predicate is equivalent to writing a separate `match each` step, but a marker composes and a step does not. A marker can sit inside a larger expected payload, several levels down, next to literals and type markers, and still produce one failure report with the exact path. A separate `match each` has to name its own left-hand side, which means pulling the array out of the payload and asserting it in isolation — more steps, and a report that no longer shows the array's context.

  • What does `'#[3]'` do differently from `'#[_ == 3]'`?
    Nothing — they are the same assertion. A bracket body with no standalone `_` is rewritten as `<body> == _` before it is evaluated, so a bare count becomes a comparison against the length. Spelling the placeholder out only earns its keep when the rule is not equality, as in `'#[_ > 0]'` or `'#[_ <= 100]'`.
  • Does `'#[]? _ < 5'` still assert anything when the value is not an array?
    Yes, and that is the one thing the empty-bracket form does assert. Karate checks that the actual value is a list before it evaluates anything in either slot, and fails with `actual is not an array` if it is not. So `'#[]'` on its own is the idiomatic way to say "this must be an array", with no rule about size or members.

saying these in an interview costs you the question

  • Reads both forms as a per-element check
  • Thinks the brackets can only hold a literal count
  • Expects a bracket expression to see each element
  • Assumes the second form also pins the array length
  • Believes a bracket rule can reference a field of an element