skip to content

Payload Matching

One step states what a whole response body should look like, with markers standing in for the values that change every run. Interviewers probe how strict you make that claim.

on this pageshow

explore

questions

29

In a Karate feature file, what does `match response contains { id: 1 }` assert, and what does `match response.ids contains 23` assert when `ids` is a JSON array?

level: juniorimportance: must knowfreq 76%

answer

  1. a subset check, not a whole-payload check
  2. extra keys on the actual side are ignored
  3. arrays: membership, order irrelevant
  4. a bare scalar is wrapped into a list
  5. plain strings fall back to substring

basics

~20 s

Karate's contains asserts a subset. Against a JSON object, every expected key must exist with an equal value while extra actual keys are ignored. Against a JSON array, every expected item must appear somewhere, in any order.

solid answer

~40 s

`match response contains { id: 1 }` asserts only that the response object has an `id` key equal to `1`; any other key the service returns is never looked at, which is exactly what separates it from `match response == { id: 1 }`. Against a JSON array, `match response.ids contains 23` is a membership test: every expected item must be found somewhere in the actual array, order does not matter, and the actual array may be longer. Karate wraps a bare scalar on the right into a one-element list, so `contains 23` and `contains [23]` behave identically. Against a plain string, `contains` degrades to an ordinary substring test. An empty expected object, `contains {}`, always passes.

code

gherkin · 23 lines
gherkin
* def cat =
  """
  {
    name: 'Billie',
    kittens: [
      { id: 23, name: 'Bob' },
      { id: 42, name: 'Wild' }
    ]
  }
  """

# object: only the named keys are checked
* match cat contains { name: 'Billie' }

# array: order and size of the actual array do not matter
* match cat.kittens[*].id contains 23
* match cat.kittens[*].id contains [42, 23]

# whole objects can be looked for inside an array
* match cat.kittens contains { id: 42, name: 'Wild' }

# an empty expected object asserts nothing at all
* match cat contains {}

go deeper

for a junior

Recall that contains checks a subset: named keys must be there, unnamed keys are ignored, and array order does not matter. Being able to write the step and say what it does not check is enough at this level.

for a middle

Explain the mechanics: the walk is driven by the expected side, a scalar on the right is auto-wrapped into a one-element list, and a string actual falls back to a substring test.

for a senior

Watch for the assertions that quietly stop asserting — an expected object that collapses to {}, or a one-character expected string on a string actual. Both stay green forever.

for a principal

Set the house convention for when a suite reaches for contains versus ==, and make sure the convention is written down, because the two read almost identically in review.

## The two comparison families Karate's `match` is built into the language — there is no step-definition to write, no matcher library to import and no assertion object to build, so the choice between "compare the whole payload" and "compare a subset" is a single word inside the step. `match x == y` is a recursive deep-equality check in which the expected literal has to describe the entire value. `match x contains y` relaxes that at the level the operator is applied to: the step states what must be present and stays silent about everything else. ```gherkin * def foo = { bar: 1, baz: 'hello', ban: 'world' } * match foo contains { bar: 1 } * match foo contains { bar: 1, baz: 'hello' } # this would fail - '==' has to describe the whole object # * match foo == { bar: 1, baz: 'hello' } ``` ## JSON objects: a subset of keys With a map on the left, `contains` walks the **expected** map, not the actual one. Every key in the expected object must exist on the actual side and its value must match; keys the actual object carries that the expected object never names are never examined. - `match foo contains { bar: 1 }` passes even though `foo` also holds `baz` and `ban`. - `match foo contains { huh: 1 }` fails with `actual does not contain key - 'huh'`. - `match foo contains { bar: 2 }` fails on the **value**, not on the key. - `match foo contains { }` **always passes**. An empty expected map asserts nothing whatsoever, which is easy to produce by accident when the expected object is built from a variable or read from a file that came back empty. ## JSON arrays: membership, not position With a list on the left, `contains` is a membership test. Each expected element is searched for across the whole actual array, so **order is irrelevant** and the actual array is allowed to be longer than the expected one. | step, against `[23, 42, 7]` | result | |---|---| | `match ids contains 23` | passes | | `match ids contains [42, 23]` | passes — order does not matter | | `match ids contains [23, 42, 7]` | passes | | `match ids contains [23, 99]` | fails — `99` is nowhere in the array | Two mechanical details fall out of how that search is written: 1. A length guard runs **before** the search. If the expected list is longer than the actual list the step fails immediately with `actual array length is less than expected`. 2. Inside that guard, plain `contains` does not track multiplicity — a repeated expected item can be satisfied by the same actual element more than once. Only `contains only` marks an actual element as consumed once it has been matched. Elements are compared as whole values, so an array of objects behaves the same way: `match cat.kittens contains { id: 42, name: 'Wild' }` finds that object at any index. ## Strings, XML and the scalar shortcut When the actual value is a plain string, `contains` becomes an ordinary substring test — `match name contains 'ali'` passes for `'alice'`, and nothing JSON-ish happens. When the actual value is XML, both sides are converted to a map first, so the object rules above apply to it. When the two sides have **different** types and the operator belongs to the `contains` family, Karate wraps a non-list expected value into a one-element list before comparing. That is why `match ids contains 23` and `match ids contains [23]` are the same assertion, and why `match cat.kittens[*].name contains 'Bob'` reads naturally after a JsonPath wildcard. The wrap is deliberately skipped for expected strings that stand for an array, an object or an embedded expression — anything beginning `#[`, `##[`, `#(` or `##(`, plus `#array` and `#object` — so a marker on the right is still interpreted as a marker rather than smuggled in as a literal. ## Where the looseness stops `contains` is lenient **only at the level it is applied to**. The moment the walk descends into a nested object or array, the child comparison switches back to equality — which is the entire reason `contains deep` exists as a separate operator. The `each` modifier composes with the family too: `match each response contains { id: '#number' }` applies containment to every element of an array rather than to the array itself, and `!contains` negates the whole result. Because all of this lives in the step grammar, `Given`, `When`, `Then` and `*` are interchangeable labels in front of it — `* match ...` is as valid as `Then match ...`, and neither form is resolved against a glue registry.

  • What does `match foo contains { }` assert about `foo`?
    Nothing. An empty expected map has no keys to walk, so the step passes for any object. It is worth guarding against, because an expected payload built from a variable or read from a file can collapse to `{}` and turn a real assertion into a no-op that still shows green.
  • Does `match name contains 'ali'` work when `name` is the string `'alice'`?
    Yes. When the actual value is a string, `contains` is a plain substring test, so it passes. That also means a typo such as `match name contains 'alicee'` fails on containment rather than on a type error, and that a one-character expected string will match far more strings than you intended.
  • Is `match ids contains 23` different from `match ids contains [23]`?
    No. When the two sides have different types and the operator is in the `contains` family, Karate wraps the non-list expected value into a one-element list first, so the two steps run the identical comparison. The wrap is skipped for expected strings that denote an array, an object or an embedded expression.

saying these in an interview costs you the question

  • Says contains also requires both arrays to be the same length
  • Believes contains on a JSON array is order-sensitive
  • Thinks contains checks nested objects loosely as well
  • Claims contains only works on objects and never on arrays
  • Assumes match foo contains {} fails on a non-empty object
open as a page

In a Karate feature file, what does the step `* match order == { id: 1, name: 'Billie' }` assert, and what happens if `order` also carries a `role` key?

level: juniorimportance: must knowfreq 84%

basics

~20 s

Karate's match == compares the entire payload in one recursive step: every key and value must correspond exactly. An extra role key fails the step, because == is a strict whole-payload comparison, not a subset check.

open as a page

In a Karate feature file, what forms can the left-hand side of a `match` step take, and what does a bare `$` refer to there?

level: juniorimportance: must knowfreq 82%

basics

~20 s

The left side of a Karate match step is a variable name, a JsonPath or XPath rooted at a variable such as response.name, a function or method call, or parenthesised JavaScript. A bare $ stands for the response, and a left side that starts with / is a bare XPath rooted there too.

open as a page

In a Karate feature file, the response carries a server-generated `id` and a `createdAt` timestamp that differ on every run. How do you still assert the entire payload in a single `match` step?

level: juniorimportance: must knowfreq 78%

basics

~20 s

Put marker strings where the unpredictable values would go: match response == { id: '#uuid', createdAt: '#string', name: 'Billie' }. A marker asserts the value's type or shape instead of its content, so one step still checks every key.

open as a page

In a Karate feature file, what does the expected value `'#? _ > 0'` assert inside a `match`, and what is `_` bound to while that expression runs?

level: juniorimportance: must knowfreq 58%

basics

~20 s

#? runs the JavaScript that follows it and passes only when the result is true. Karate binds _ to the actual value at that position, so '#? _ > 0' asserts that the value there is greater than zero.

open as a page

In a Karate feature file asserting on an XML response, which XPath shapes may sit on the left of a `match` step, and what does a leading `/` alone select?

level: juniorimportance: must knowfreq 62%

basics

~20 s

Any valid XPath sits directly on the left of a Karate match step - no extractor object, no Java code. A leading slash means the response, so match /cat/name == 'Billie' equals match response /cat/name == 'Billie'.

open as a page

In a Karate feature file, `* def original = { a: 1, b: 2, c: 3, d: { a: 1, b: 2 } }`. Why does `match original contains { a: 1, d: { b: 2 } }` fail, and which operator makes it pass?

level: middleimportance: must knowfreq 60%

basics

~20 s

contains is lenient only at the level it is applied to. Descending into a nested object or array, the child comparison reverts to equality, so d must equal the expected literal exactly and fails. Use contains deep.

open as a page

In Karate's `match actual == expected`, does the order of keys in a JSON object matter, and does the order of elements in a JSON array matter?

level: middleimportance: must knowfreq 72%

basics

~20 s

Key order in a JSON object is irrelevant, because Karate looks each expected key up in the actual map by name. Element order in a JSON array is significant: lists are compared index by index after a length check.

open as a page

In a Karate feature file `cat.kittens` holds two objects with ids 23 and 42. Why does `* match cat.kittens[*].id == 23` fail, and how do you assert on just the first id?

level: middleimportance: must knowfreq 70%

basics

~20 s

The wildcard makes the path indefinite, so the left side is the array [23, 42] and never the scalar 23. Match the array, or collapse it in a def or on the right of the match with get[0] cat.kittens[*].id, which returns the first element.

open as a page

In a Karate feature file, how do the expected values `'#null'`, `'#notpresent'`, `'#present'` and `'#notnull'` differ, and which of them passes when the key is missing from the response?

level: middleimportance: must knowfreq 62%

basics

~10 s

'#null' needs the key present holding null. '#notnull' needs it present and not null. '#present' needs it present, any value including null. '#notpresent' needs it absent. Only '#notpresent' passes on a missing key.

open as a page

In a Karate feature file, `* def data = { a: 'hello', b: null, c: null }` is followed by `* def json = { foo: '#(data.a)', bar: '#(data.b)', baz: '##(data.c)' }`. What does `json` hold, and why?

level: middleimportance: must knowfreq 48%

basics

~20 s

json holds { foo: 'hello', bar: null }. The single-hash form substitutes whatever the expression evaluates to, keeping the key even when that value is null; the double-hash form deletes the key outright when its expression evaluates to null.

open as a page

In a Karate feature file with `* def date = { month: '3' }`, `match date == { month: '#? _ > 0' }` passes but `match date == { month: '#number? _ > 0' }` fails. Why?

level: middleimportance: must knowfreq 44%

basics

~20 s

The month value is the string '3'. The bare predicate passes because JavaScript coerces it to a number before comparing. The combined form runs the number validator first, and that validator does not coerce, so a string fails.

open as a page

In a Karate feature file, match //teacher[@department='science']/subject == ['math', 'physics'] passes. What does that same step produce when the science teacher has only one subject element, and why does comparing it to ['math'] then fail?

level: middleimportance: must knowfreq 46%

basics

~20 s

An XPath selecting several nodes comes back as a list, so you compare it to a JSON array. A single node is unwrapped, so with one subject the step yields the string 'math' and ['math'] fails with data types don't match.

open as a page

In a Karate feature file, `* def data = { foo: [1, 2, 3] }`. How do `match data.foo contains [3, 2]`, `match data.foo contains only [3, 2, 1]` and `match data.foo contains any [9, 2, 8]` differ?

level: middleimportance: should knowfreq 50%

basics

~20 s

contains needs every expected item present and lets the actual array be longer. contains only needs the same items in any order and the same length, so a shorter expected list fails. contains any needs at least one expected item.

open as a page

In a Karate feature file, `* match each items == { active: true }` runs when `items` is an empty array. Does the step pass or fail, and how do you change that?

level: middleimportance: should knowfreq 46%

basics

~10 s

The step fails. Karate's match each rejects an empty array by default and reports 'match each failed, empty array / list'. Adding the step configure matchEachEmptyAllowed = true makes an empty list pass vacuously.

open as a page

In a Karate feature file, `* match order == { id: 1 }` fails with `data types don't match`. What causes that, and what does `* match order != { id: 1 }` do in the same situation?

level: middleimportance: should knowfreq 40%

basics

~20 s

Karate compares the two sides' data types before their contents, so a string on the left and an object on the right can never be equal and the step reports data types don't match. The != form passes for exactly that reason.

open as a page

In a Karate feature file, what is the difference between `get cat.kittens[*].id`, the `$cat.kittens[*].id` short-cut, and `karate.jsonPath(cat, expression)` — and when do you need the last one?

level: middleimportance: should knowfreq 56%

basics

~20 s

get and the $ short-cut both apply a JsonPath, written as literal step text, to a named variable. karate.jsonPath takes the path as a string argument, so use it when the path is built at runtime.

open as a page

In a Karate feature file, what do the expected values `'#[]'`, `'#[3]'` and `'#[] catSchema'` each assert about a JSON array in the response?

level: middleimportance: should knowfreq 48%

basics

~20 s

'#[]' asserts the value is an array. '#[3]' adds that its length is 3. '#[] catSchema' evaluates catSchema as an expression and matches every element against the result, which is the same as writing match each.

open as a page

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

level: middleimportance: should knowfreq 33%

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.

open as a page

In a Karate feature file, why can the XPath written into a match step not contain a variable, and what does karate.xmlPath(xml, expression) give you instead?

level: middleimportance: should knowfreq 36%

basics

~20 s

A match step's path is read literally from the step text, so nothing is interpolated. karate.xmlPath takes the path as an ordinary JavaScript string, so you can build it by concatenation, assign the result, and assert on that.

open as a page

In a Karate feature file, what type of value does an XPath on the left of a match produce for a leaf element, for an element with children, for an attribute, and for count()?

level: middleimportance: should knowfreq 40%

basics

~20 s

A leaf element yields its text as a string, so numbers arrive quoted. An element with children yields an XML chunk compared against an XML literal. An attribute yields its value as a string. XPath count() yields a number.

open as a page

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%

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.

open as a page

In a Karate feature file, `* def foo = [1, 2, 3, 5]`. Does `match foo !contains [5, 6]` pass, and what exactly does `!contains` negate?

level: seniorimportance: should knowfreq 40%

basics

~10 s

It passes, which surprises most people. !contains negates the whole contains result, and contains requires every expected item. Since 6 is missing, contains fails and the negation succeeds even though 5 is present.

open as a page

A Karate step `* match order == expected` fails on a deeply nested JSON body. What does Karate's `match failed:` report give you beyond "the two payloads differ"?

level: seniorimportance: should knowfreq 52%

basics

~20 s

Karate prints the match type, then one indented block per level of the failure path: the JsonPath, the reason, the two data types, and the actual and expected values at that node, ending at the exact leaf that differed.

open as a page

A Karate step reads `* def id = get[0] $.items[?(@.type == 'special')].id` and the `match` after it passes in CI, even though no item in the response has that type. What is Karate doing, and how do you stop a bad filter from passing silently?

level: seniorimportance: should knowfreq 44%

basics

~20 s

A filter that matches nothing yields an empty list, not an error, and get[N] hands it back untouched; a missing property degrades to #notpresent. A bad path produces a value, so the assertion can pass vacuously.

open as a page

A team wants their Karate feature file to validate a response against their published JSON Schema document. What does Karate actually provide for that?

level: seniorimportance: should knowfreq 44%

basics

~20 s

Karate ships no JSON-Schema validator and no XSD or DTD matcher. Its whole schema story is schema by example: you def an expected payload made of marker strings and match the response against it, checking shape and content in one step.

open as a page

In a Karate feature file, a predicate such as `'#? _ == $.celsius * 1.8 + 32'` reaches a sibling field under a plain `match`, but the same `$` path no longer reaches one under `match each`. Why, and which binding replaces it?

level: seniorimportance: should knowfreq 29%

basics

~20 s

The dollar sign is pinned to the root of the match statement's actual side, which under match each is the whole array, not the element. Karate binds _$ to the current element, so sibling fields go through _$.

open as a page

A SOAP response is wrapped in namespace prefixes such as soapenv: and acc:. How do you write the XPath on the left of a Karate match step against it, and what happens to those prefixes on the right side?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Write the XPath with no prefixes at all. Karate parses XML namespace-unaware by default, so /Envelope/Body/getAccountByPhoneNumber reaches soapenv:Envelope and acc:getAccountByPhoneNumber. On the right side, prefixes and xmlns attributes are stripped from both documents before they are compared.

open as a page

In a Karate feature file, what happens when an expected payload contains a marker name Karate does not recognise, such as `'#numbr'` or `'##anythng'`?

level: seniorimportance: nice to knowfreq 31%

basics

~20 s

Nothing reports the typo. An unrecognised single-hash name degrades to a literal string comparison against the actual value, and a double-hash name is skipped outright whenever the key is absent, so the assertion can silently check nothing.

open as a page