skip to content

In a Karate `match`, what do the inline markers `'#(^part)'`, `'#(^^part)'`, `'#(^*part)'` and `'#(^+part)'` mean, and what can they express that the `contains` step spellings cannot?

level: seniorimportance: should knowfreq 32%

answer

  1. the operator moves inside the expected literal
  2. prefixes on an embedded expression
  3. a loose field inside a strict object
  4. containment nested inside containment
  5. two deep operators have no prefix

basics

~20 s

They embed a containment operator inside an expected literal: caret is contains, double caret is contains only, caret-star is contains any, caret-plus is contains deep, bang-caret is not-contains. They apply containment at a nested position, unreachable by a step-level operator.

solid answer

~50 s

Karate lets the containment operators appear inside an expected value as an embedded expression, so `match actual == '#(^part)'` is the same assertion as `match actual contains part`. The prefixes map one-to-one: `^` is `contains`, `^^` is `contains only`, `^*` is `contains any`, `^+` is `contains deep` and `!^` is `!contains`. Two things only the inline form can say. First, containment at a **nested position** inside a larger equality literal — `match response == { id: '#number', kittens: '#(^^created)' }` pins the shape of the whole body while comparing one field order-insensitively. Second, containment **inside** containment: `match actual contains '#(^part)'` means the array holds an element that itself contains `part`, which has no step-level spelling. `'#[] ^partSchema'` is the each-element form, and it takes a marker rather than a literal because `each` applies the containment to every element of the array.

code

gherkin · 23 lines
gherkin
* def actual = [{ a: 1, b: 'x' }, { a: 2, b: 'y' }]
* def part = { a: 1 }
* def partSchema = { a: '#number' }
* def first = { a: 1, b: 'x' }
* def shuffled = [{ a: 2, b: 'y' }, { b: 'x', a: 1 }]

# each inline form is the same assertion as the step above it
* match actual contains first
* match actual == '#(^first)'

* match actual contains only shuffled
* match actual == '#(^^shuffled)'

* match actual contains deep part
* match actual == '#(^+part)'

# no step-level equivalent: an element that itself contains 'part'
* match actual contains '#(^part)'

# the each form drops the parentheses - and needs an expected value that
# holds for EVERY element, so the marker and not the literal 'part'
* match each actual contains partSchema
* match actual == '#[] ^partSchema'

go deeper

for a junior

Recognise that a caret inside an embedded expression means a containment operator rather than JsonPath. Reading '#(^part)' as 'contains part' is enough at this level.

for a middle

Map each prefix to its step operator and explain that the text after the prefix is evaluated as JavaScript in scenario scope before the comparison runs.

for a senior

Use the inline form where it earns its keep — one loose field inside an otherwise exact object, or a containment nested inside a containment — and not as a terser way of writing a plain step.

for a principal

Decide how much of this notation a team should carry. It buys reusable schema variables at four strictness levels, at the cost of a syntax that reads as noise to anyone who has not seen the prefix table.

## The operator, moved inside the payload Every containment operator in Karate has a two-character (or one-character) prefix that can be written inside an **embedded expression** on the expected side. The right-hand side is a string of the form `'#(<prefix><expression>)'`; Karate strips the prefix, evaluates the rest as JavaScript in scenario scope, and runs the resulting value through the operator the prefix named. | inline marker | equivalent step | |---|---| | `'#(^part)'` | `match actual contains part` | | `'#(^^part)'` | `match actual contains only part` | | `'#(^*part)'` | `match actual contains any part` | | `'#(^+part)'` | `match actual contains deep part` | | `'#(!^part)'` | `match actual !contains part` | | `'#[] ^partSchema'` | `match each actual contains partSchema` | Note the `each` row: inside the array-marker form the prefix is written **without** parentheses, directly after `#[]`. The same shape gives `'#[] ^*mixSchema'` for `each contains any` and `'#[] !^badSchema'` for `each !contains`. The `each` rows take a marker rather than a literal on purpose: `each` applies the containment to **every** element, so an expected value such as `{ a: 1 }` has to hold for all of them. Against `[{ a: 1, b: 'x' }, { a: 2, b: 'y' }]` it does not — the child comparison for a named key is equality, `2` is not `1`, and the step fails at index 1. ## What only the inline form can say If every marker had a step equivalent the feature would be sugar. It has two jobs that the step grammar cannot do at all. **1. Containment at a nested position.** A step-level operator applies to the whole comparison. The inline form applies to one field: ```gherkin * def created = [{ id: 23, name: 'Bob' }, { id: 42, name: 'Wild' }] * match response == { id: '#number', name: 'Billie', kittens: '#(^^created)' } ``` That single step pins the exact shape of the response object — no extra keys, `id` a number, `name` exactly `'Billie'` — while comparing `kittens` order-insensitively. There is no way to write that with one step-level operator, because the operator would have to be `==` and `contains only` at the same time. **2. Containment inside containment.** The two forms compose in the other direction as well: ```gherkin * def actual = [{ a: 1, b: 'x' }, { a: 2, b: 'y' }] * def part = { a: 1 } * match actual contains '#(^part)' ``` The outer `contains` scans the array; the inner `^` makes each element compared with containment rather than equality. In words: *the array holds an element that itself contains `part`.* Written as `match actual contains part` it would fail, because the element `{ a: 1, b: 'x' }` would have to **equal** `{ a: 1 }`. The upstream test suite marks exactly these lines `no in-line equivalent!` in the other direction — they are the cases with no step spelling. `'#(^*mix)'` and `'#(^+part)'` compose the same way inside an outer `contains`. ## Where it stops - **Two operators have no inline shortcut at all**: `contains only deep` and `contains any deep`. The prefix table simply has no entry for them. `contains only deep` is still writable as a step. `contains any deep` is not, on Karate 1.x: the step parser recognises exactly four suffixes — `only deep`, `only`, `any`, `deep` — with no `any deep` rung, so `match x contains any deep y` matches `any`, swallows the word `deep` into the expected expression and degrades silently to `contains any`. It became a real step spelling only in 2.x. - The prefix is matched longest-first, so `^^`, `^*` and `^+` are recognised before the bare `^`. A stray space — `'#( ^part)'` — is not the same string and will not be read as a prefix. - The whole marker is an ordinary single-quoted string in the feature file. That is what lets it sit as a **value** inside a JSON literal, and it is also why a missing quote turns the caret into a syntax error rather than into a silently different assertion. - `'#(...)'` with no prefix is plain equality against the evaluated value, which is the ordinary embedded-expression form; the caret family is a strict extension of it. - The expression inside the parentheses is JavaScript evaluated in scenario scope, so it is normally a variable name, but any expression that yields a map, list or string works. ## Why this exists It is the same design idea as Karate's `#`-markers generally: the expected value is a literal you can read, and the operators live inside it rather than in a matcher chain built up in Java. A schema file becomes a reusable variable, and the caret prefixes let one schema be applied strictly in one assertion and loosely in another without editing the schema itself: ```gherkin * def schema = { a: '#number', b: '#string' } * def partSchema = { a: '#number' } * match actual[0] == schema * match actual[0] == '#(^partSchema)' * match actual == '#[] schema' * match actual == '#[] ^partSchema' ``` Read top to bottom, those four lines are exact-shape, partial-shape, exact-shape-for-every-element and partial-shape-for-every-element — four different strictnesses over two reusable variables, with no assertion objects anywhere. The cost is legibility: a reviewer who has not memorised the prefix table sees `'#(^^created)'` as noise, so the notation earns its place where the step grammar genuinely cannot express the assertion, and rarely anywhere else.

  • How does `match actual contains '#(^part)'` differ from `match actual contains part`?
    The second compares each array element with `part` using equality, so an element carrying extra keys fails. The first makes the inner comparison containment as well, so it reads as 'the array holds an element that itself contains `part`'. That nesting of one containment inside another has no step-level spelling.
  • Which containment operators have no inline prefix?
    `contains only deep` and `contains any deep`. The prefix table covers `^` for contains, `^^` for contains only, `^*` for contains any, `^+` for contains deep and `!^` for not-contains, and stops there. `contains only deep` can still be written as a step-level operator. `contains any deep` cannot, on Karate 1.x: the step parser's suffix ladder is `only deep`, `only`, `any`, `deep`, with no `any deep` rung, so the step degrades silently to `contains any` with `deep` swallowed into the expected expression. It became a real step spelling only in 2.x.
  • What does `'#[] ^partSchema'` assert?
    That the actual value is an array and every element contains `partSchema` — the inline equivalent of `match each actual contains partSchema`. Inside the `#[]` array marker the prefix is written directly, with no parentheses, and `'#[] ^*mixSchema'` and `'#[] !^badSchema'` follow the same shape.

saying these in an interview costs you the question

  • Thinks the caret prefixes are JsonPath syntax
  • Reads #(^x) as an exclusion or a negation
  • Assumes every inline prefix has a step equivalent
  • Writes #(^^ x) with a space and expects the prefix to parse
  • Believes contains only deep has an inline shortcut too