diff --git a/README.md b/README.md index a39004be9..ceef258c2 100755 --- a/README.md +++ b/README.md @@ -186,6 +186,7 @@ And you don't need to create additional Java classes for any of the payloads tha | match contains only deep | match !contains | match each + | match each contains deep | match header | Fuzzy Matching | Schema Validation @@ -3017,6 +3018,42 @@ Symbol | Evaluates To There is a shortcut for `match each` explained in the next section that can be quite useful, especially for 'in-line' schema-like validations. +#### `match each contains deep` +`match each` can be combined with `contains deep` so that for each JSON object a “deep contains” match is performed within nested lists or objects. + +This is useful for testing payloads with JSON arrays whose members have a few essential keys that you wish to validate. + +```cucumber + Given def response = + """ + [ + { + "a": 1, + "arr": [ + { + "b": 2, + "c": 3 + } + ] + }, + { + "a": 1, + "arr": [ + { + "b": 2, + "c": 3 + }, + { + "b": 4, + "c": 5 + } + ] + } + ] + """ + Then match each response contains deep { a: 1, arr: [ { b: 2 } ] } +``` + ## Schema Validation Karate provides a far more simpler and more powerful way than [JSON-schema](http://json-schema.org) to validate the structure of a given payload. You can even mix domain and conditional validations and perform all assertions in a single step. diff --git a/karate-core/src/main/java/com/intuit/karate/MatchStep.java b/karate-core/src/main/java/com/intuit/karate/MatchStep.java index f5670af9b..ab6db5565 100644 --- a/karate-core/src/main/java/com/intuit/karate/MatchStep.java +++ b/karate-core/src/main/java/com/intuit/karate/MatchStep.java @@ -139,6 +139,12 @@ private static Match.Type getType(boolean each, boolean not, boolean contains, b if (any) { return Match.Type.EACH_CONTAINS_ANY; } + if (deep) { + if (not) { + throw new RuntimeException("'each !contains deep' is not yet supported, use 'each contains deep' instead"); + } + return Match.Type.EACH_CONTAINS_DEEP; + } return not ? Match.Type.EACH_NOT_CONTAINS : Match.Type.EACH_CONTAINS; } return not ? Match.Type.EACH_NOT_EQUALS : Match.Type.EACH_EQUALS; diff --git a/karate-core/src/test/java/com/intuit/karate/MatchStepTest.java b/karate-core/src/test/java/com/intuit/karate/MatchStepTest.java index 3948205ce..dad9fce9b 100644 --- a/karate-core/src/test/java/com/intuit/karate/MatchStepTest.java +++ b/karate-core/src/test/java/com/intuit/karate/MatchStepTest.java @@ -25,6 +25,7 @@ void testMatchStep() { test("hello world == foo", EQUALS, "hello", "world", "foo"); test("hello world contains only deep foo", CONTAINS_ONLY_DEEP, "hello", "world", "foo"); test("each hello world == foo", EACH_EQUALS, "hello", "world", "foo"); + test("each hello world contains deep foo", EACH_CONTAINS_DEEP, "hello", "world", "foo"); test("{\"a\":1,\"b\":2} == '#object'", EQUALS, "({\"a\":1,\"b\":2})", null, "'#object'"); test("hello.foo(bar) != blah", NOT_EQUALS, "hello.foo(bar)", null, "blah"); test("foo count(/records//record) contains any blah", CONTAINS_ANY, "foo", "count(/records//record)", "blah"); diff --git a/karate-core/src/test/java/com/intuit/karate/core/ScenarioRuntimeTest.java b/karate-core/src/test/java/com/intuit/karate/core/ScenarioRuntimeTest.java index 59c2c9c32..90d2f7d44 100644 --- a/karate-core/src/test/java/com/intuit/karate/core/ScenarioRuntimeTest.java +++ b/karate-core/src/test/java/com/intuit/karate/core/ScenarioRuntimeTest.java @@ -765,7 +765,15 @@ void testMatchContainsOnlyDeep() { "def response = { foo: [ 'a', 'b' ] } ", "match response contains only deep { foo: [ 'b', 'a' ] }" ); - } + } + + @Test + void testMatchEachContainsDeep() { + run( + "def response = [ { a: 1, arr: [ { b: 2, c: 3 }, { b: 4, c: 5 } ] } ]", + "match each response contains deep { a: 1, arr: [ { b: '#number', c: 3 } ] }" + ); + } @Test void testJavaInteropStatic() {