Karate match contains vs match equals for dynamic JSON fields
0 reputation · 16 Aug 2020, 18:47 UTC
0 reputation · 16 Aug 2020, 18:47 UTC
Validate JSON API responses in Karate DSL while tolerating server-added dynamic fields such as timestamps and generated IDs.
The project uses Karate’s built-in JSON matchers. `match equals` requires an exact body match and fails when the response includes any undeclared key. `match contains` asserts only the expected subset and ignores extra fields, but it does not prove that no additional fields exist. The team needs to choose one default matcher for contract tests without introducing brittleness or hiding response bloat.
Assuming a recent stable Karate release where both operators behave consistently for JSON, which matcher should be the project default, and under what conditions should the stricter operator override it?
35775 reputation · 17 Aug 2020, 06:40 UTC
match containsUse match contains as the project default for contract tests. It asserts every expected key and value — including deeply nested paths — while ignoring extra fields the server adds (timestamps, generated IDs, trace headers). This prevents false failures from dynamic data without sacrificing coverage of the declared contract.
match contains Protects Against Field Removalmatch contains is a deep subset check. If any expected key is missing at any nesting level, the match fails. It only relaxes the "no extra keys" rule. For example:
# Expected
{ id: '#uuid', user: { name: 'Alice', email: '[contact removed]' } }
# Actual (passes — extra field ignored)
{ id: '123', user: { name: 'Alice', email: '[contact removed]' }, createdAt: '2026-11-13' }
# Actual (fails — nested email missing)
{ id: '123', user: { name: 'Alice' } }
This behavior is stable in Karate 1.0+ (current 1.4.x).
match equalsReserve match equals (or match ==) for endpoints whose OpenAPI/JSON Schema explicitly declares additionalProperties: false or where the team has formally agreed the contract is closed. In those cases, extra fields represent a contract violation and should fail the test.
Custom matchers registered via karate.configure or Java helpers add reusable predicates (e.g., #isoTimestamp, #uuid) but do not change the core semantics of contains vs equals. They apply globally, so keep predicates pure and unit-tested — a buggy predicate can silently mask failures.
Combine match contains expected with a JSON Schema validation step rather than switching to equals:
* def schema = read('contract-schema.json')
* match contains response == expected
* karate.match(response, schema)
This gives you subset semantics for dynamic fields plus strict schema enforcement for the declared contract.
match contains on arrays: each expected element must be found somewhere in the actual array (deep contains, order-insensitive).match contains only when array order and cardinality must match exactly.* print karate.version — confirm 1.0+.equals on same payload (fail).additionalProperties: false only where you intend equals.karate --tags @contract after changing the default.Does your OpenAPI spec already define additionalProperties: false for any endpoints? If yes, those are the only candidates for match equals; everything else stays on contains.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.