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?
answer
- the operator moves inside the expected literal
- prefixes on an embedded expression
- a loose field inside a strict object
- containment nested inside containment
- two deep operators have no prefix
basics
~20 sThey 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 sKarate 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* 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
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.
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.
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.
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