diff --git a/json-path-assert/README.md b/json-path-assert/README.md new file mode 100644 index 00000000..8847deb4 --- /dev/null +++ b/json-path-assert/README.md @@ -0,0 +1,82 @@ +json-path-assert +================ + +A library with [hamcrest-matchers](http://hamcrest.org/JavaHamcrest/) for JsonPath. + +# Getting started + +This library is available at the Central Maven Repository. Maven users add this to your POM. + +```xml + + com.jayway.jsonpath + json-path-assert + 2.1.0 + +``` + +# Usage guide + +Statically import the library entry point: + + import static com.jayway.jsonpath.matchers.JsonPathMatchers.*; + +NOTE: The actual evaluation of JsonPath will depend on the current configuration: + + Configuration.setDefaults(...); + +The matchers can be used to inspect different representations of JSON: + + // As a String... + String json = ...; + + // or a file... + File json = ...; + + // or an already parsed json object... + Object json = Configuration.defaultConfiguration().jsonProvider().parse(content); + +Usage examples: + + // Verify validity of JSON + assertThat(json, isJson()); + + // Verify existence (or non-existence) of JSON path + assertThat(json, hasJsonPath("$.message")); + assertThat(json, hasNoJsonPath("$.message")); + + // Verify evaluation of JSON path + assertThat(json, hasJsonPath("$.message", equalTo("Hi there"))); + assertThat(json, hasJsonPath("$.quantity", equalTo(5))); + assertThat(json, hasJsonPath("$.price", equalTo(34.56))); + assertThat(json, hasJsonPath("$.store.book[*].author", hasSize(4))); + assertThat(json, hasJsonPath("$.store.book[*].author", hasItem("Evelyn Waugh"))); + +Combine matchers for greater expressiveness + + // This will separate the JSON parsing from the path evaluation + assertThat(json, isJson(withoutJsonPath("..."))); + assertThat(json, isJson(withJsonPath("...", equalTo(3)))); + + // Combine several JSON path evaluations into a single statement + // (This will parse the JSON only once) + assertThat(json, isJson(allOf( + withJsonPath("$.store.name", equalTo("Little Shop")), + withoutJsonPath("$.expensive"), + withJsonPath("$..title", hasSize(4))))); + +Match on pre-compiled complex JSON path expressions + + Filter cheapFictionFilter = filter( + where("category").is("fiction").and("price").lte(10D)); + JsonPath cheapFiction = JsonPath.compile("$.store.book[?]", cheapFictionFilter); + String json = ...; + assertThat(json, isJson(withJsonPath(cheapFiction))); + +Use typed matchers for specific JSON representations, if needed + + String json = ... + assertThat(json, isJsonString(withJsonPath("$..author"))); + + File json = ... + assertThat(json, isJsonFile(withJsonPath("$..author"))); diff --git a/json-path-assert/src/main/java/com/jayway/jsonpath/matchers/IsJson.java b/json-path-assert/src/main/java/com/jayway/jsonpath/matchers/IsJson.java new file mode 100644 index 00000000..c46eb07c --- /dev/null +++ b/json-path-assert/src/main/java/com/jayway/jsonpath/matchers/IsJson.java @@ -0,0 +1,65 @@ +package com.jayway.jsonpath.matchers; + +import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.JsonPathException; +import com.jayway.jsonpath.ReadContext; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +import java.io.File; +import java.io.IOException; + +public class IsJson extends TypeSafeMatcher { + private final Matcher jsonMatcher; + + public IsJson(Matcher jsonMatcher) { + this.jsonMatcher = jsonMatcher; + } + + @Override + protected boolean matchesSafely(T json) { + try { + ReadContext context = parse(json); + return jsonMatcher.matches(context); + } catch (JsonPathException e) { + return false; + } catch (IOException e) { + return false; + } + } + + public void describeTo(Description description) { + description.appendText("is json ").appendDescriptionOf(jsonMatcher); + } + + @Override + protected void describeMismatchSafely(T json, Description mismatchDescription) { + try { + ReadContext context = parse(json); + jsonMatcher.describeMismatch(context, mismatchDescription); + } catch (JsonPathException e) { + buildMismatchDescription(json, mismatchDescription, e); + } catch (IOException e) { + buildMismatchDescription(json, mismatchDescription, e); + } + } + + private static void buildMismatchDescription(Object json, Description mismatchDescription, Exception e) { + mismatchDescription + .appendText("was ") + .appendValue(json) + .appendText(" which failed with ") + .appendValue(e.getMessage()); + } + + private static ReadContext parse(Object object) throws IOException { + if (object instanceof String) { + return JsonPath.parse((String) object); + } else if (object instanceof File) { + return JsonPath.parse((File) object); + } else { + return JsonPath.parse(object); + } + } +} diff --git a/json-path-assert/src/main/java/com/jayway/jsonpath/matchers/JsonPathMatchers.java b/json-path-assert/src/main/java/com/jayway/jsonpath/matchers/JsonPathMatchers.java new file mode 100644 index 00000000..c89c42cc --- /dev/null +++ b/json-path-assert/src/main/java/com/jayway/jsonpath/matchers/JsonPathMatchers.java @@ -0,0 +1,73 @@ +package com.jayway.jsonpath.matchers; + +import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.Predicate; +import com.jayway.jsonpath.ReadContext; +import org.hamcrest.Matcher; + +import java.io.File; + +import static org.hamcrest.Matchers.*; + +public class JsonPathMatchers { + + private JsonPathMatchers() { + throw new AssertionError("prevent instantiation"); + } + + public static Matcher hasJsonPath(String jsonPath) { + return describedAs("has json path %0", + hasJsonPath(jsonPath, not(anyOf(nullValue(), empty()))), + jsonPath); + } + + public static Matcher hasJsonPath(final String jsonPath, final Matcher resultMatcher) { + return isJson(withJsonPath(jsonPath, resultMatcher)); + } + + public static Matcher hasNoJsonPath(String jsonPath) { + return isJson(withoutJsonPath(jsonPath)); + } + + public static Matcher isJson() { + return isJson(withJsonPath("$..*")); + } + + public static Matcher isJson(final Matcher matcher) { + return new IsJson(matcher); + } + + public static Matcher isJsonString(final Matcher matcher) { + return new IsJson(matcher); + } + + public static Matcher isJsonFile(final Matcher matcher) { + return new IsJson(matcher); + } + + public static Matcher withJsonPath(String jsonPath, Predicate... filters) { + return withJsonPath(JsonPath.compile(jsonPath, filters)); + } + + public static Matcher withJsonPath(JsonPath jsonPath) { + return describedAs("with json path %0", + withJsonPath(jsonPath, not(anyOf(nullValue(), empty()))), + jsonPath.getPath()); + } + + public static Matcher withoutJsonPath(String jsonPath, Predicate... filters) { + return withoutJsonPath(JsonPath.compile(jsonPath, filters)); + } + + public static Matcher withoutJsonPath(JsonPath jsonPath) { + return new WithoutJsonPath(jsonPath); + } + + public static Matcher withJsonPath(String jsonPath, Matcher resultMatcher) { + return withJsonPath(JsonPath.compile(jsonPath), resultMatcher); + } + + public static Matcher withJsonPath(final JsonPath jsonPath, final Matcher resultMatcher) { + return new WithJsonPath(jsonPath, resultMatcher); + } +} diff --git a/json-path-assert/src/main/java/com/jayway/jsonpath/matchers/WithJsonPath.java b/json-path-assert/src/main/java/com/jayway/jsonpath/matchers/WithJsonPath.java new file mode 100644 index 00000000..73acaaad --- /dev/null +++ b/json-path-assert/src/main/java/com/jayway/jsonpath/matchers/WithJsonPath.java @@ -0,0 +1,60 @@ +package com.jayway.jsonpath.matchers; + +import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.JsonPathException; +import com.jayway.jsonpath.PathNotFoundException; +import com.jayway.jsonpath.ReadContext; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +public class WithJsonPath extends TypeSafeMatcher { + private final JsonPath jsonPath; + private final Matcher resultMatcher; + + public WithJsonPath(JsonPath jsonPath, Matcher resultMatcher) { + this.jsonPath = jsonPath; + this.resultMatcher = resultMatcher; + } + + @Override + protected boolean matchesSafely(ReadContext context) { + try { + T value = context.read(jsonPath); + return resultMatcher.matches(value); + } catch (JsonPathException e) { + return false; + } + } + + public void describeTo(Description description) { + description + .appendText("with json path ") + .appendValue(jsonPath.getPath()) + .appendText(" evaluated to ") + .appendDescriptionOf(resultMatcher); + } + + @Override + protected void describeMismatchSafely(ReadContext context, Description mismatchDescription) { + try { + T value = jsonPath.read(context.json()); + mismatchDescription + .appendText("json path ") + .appendValue(jsonPath.getPath()) + .appendText(" was evaluated to ") + .appendValue(value); + } catch (PathNotFoundException e) { + mismatchDescription + .appendText("json path ") + .appendValue(jsonPath.getPath()) + .appendText(" was not found in ") + .appendValue(context.json()); + } catch (JsonPathException e) { + mismatchDescription + .appendText("was ") + .appendValue(context.json()); + } + } + +} diff --git a/json-path-assert/src/main/java/com/jayway/jsonpath/matchers/WithoutJsonPath.java b/json-path-assert/src/main/java/com/jayway/jsonpath/matchers/WithoutJsonPath.java new file mode 100644 index 00000000..b339d5df --- /dev/null +++ b/json-path-assert/src/main/java/com/jayway/jsonpath/matchers/WithoutJsonPath.java @@ -0,0 +1,36 @@ +package com.jayway.jsonpath.matchers; + +import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.JsonPathException; +import com.jayway.jsonpath.ReadContext; +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import static org.hamcrest.Matchers.empty; + +public class WithoutJsonPath extends TypeSafeDiagnosingMatcher { + private final JsonPath jsonPath; + + public WithoutJsonPath(JsonPath jsonPath) { + this.jsonPath = jsonPath; + } + + @Override + protected boolean matchesSafely(ReadContext actual, Description mismatchDescription) { + try { + Object value = actual.read(jsonPath); + mismatchDescription + .appendText(jsonPath.getPath()) + .appendText(" was evaluated to ") + .appendValue(value); + return value == null || empty().matches(value); + } catch (JsonPathException e) { + return true; + } + } + + @Override + public void describeTo(Description description) { + description.appendText("without json path ").appendValue(jsonPath.getPath()); + } +} diff --git a/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/DemoTest.java b/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/DemoTest.java new file mode 100644 index 00000000..1709e195 --- /dev/null +++ b/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/DemoTest.java @@ -0,0 +1,81 @@ +package com.jayway.jsonpath.matchers; + +import org.junit.Ignore; +import org.junit.Test; + +import java.io.File; + +import static com.jayway.jsonpath.matchers.JsonPathMatchers.*; +import static com.jayway.jsonpath.matchers.helpers.ResourceHelpers.resource; +import static com.jayway.jsonpath.matchers.helpers.ResourceHelpers.resourceAsFile; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +@Ignore +public class DemoTest { + @Test + public void shouldFailOnJsonString() { + String json = resource("books.json"); + assertThat(json, isJson(withJsonPath("$.store.name", equalTo("The Shop")))); + } + + @Test + public void shouldFailOnJsonFile() { + File json = resourceAsFile("books.json"); + assertThat(json, isJson(withJsonPath("$.store.name", equalTo("The Shop")))); + } + + @Test + public void shouldFailOnInvalidJsonString() { + String json = resource("invalid.json"); + assertThat(json, isJson(withJsonPath("$.store.name", equalTo("The Shop")))); + } + + @Test + public void shouldFailOnInvalidJsonFile() { + File json = resourceAsFile("invalid.json"); + assertThat(json, isJson(withJsonPath("$.store.name", equalTo("The Shop")))); + } + + @Test + public void shouldFailOnTypedJsonString() { + String json = resource("books.json"); + assertThat(json, isJsonString(withJsonPath("$.store.name", equalTo("The Shop")))); + } + + @Test + public void shouldFailOnTypedJsonFile() { + File json = resourceAsFile("books.json"); + assertThat(json, isJsonFile(withJsonPath("$.store.name", equalTo("The Shop")))); + } + + @Test + public void shouldFailOnTypedInvalidJsonString() { + String json = resource("invalid.json"); + assertThat(json, isJsonString(withJsonPath("$.store.name", equalTo("The Shop")))); + } + + @Test + public void shouldFailOnTypedInvalidJsonFile() { + File json = resourceAsFile("invalid.json"); + assertThat(json, isJsonFile(withJsonPath("$.store.name", equalTo("The Shop")))); + } + + @Test + public void shouldFailOnNonExistingJsonPath() { + String json = resource("books.json"); + assertThat(json, hasJsonPath("$.not-here")); + } + + @Test + public void shouldFailOnExistingJsonPath() { + String json = resource("books.json"); + assertThat(json, hasNoJsonPath("$.store.name")); + } + + @Test + public void shouldFailOnExistingJsonPathAlternative() { + String json = resource("books.json"); + assertThat(json, isJson(withoutJsonPath("$.store.name"))); + } +} diff --git a/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/IsJsonFileTest.java b/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/IsJsonFileTest.java new file mode 100644 index 00000000..01afa415 --- /dev/null +++ b/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/IsJsonFileTest.java @@ -0,0 +1,75 @@ +package com.jayway.jsonpath.matchers; + +import com.jayway.jsonpath.Configuration; +import com.jayway.jsonpath.matchers.helpers.StrictParsingConfiguration; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.StringDescription; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; + +import static com.jayway.jsonpath.matchers.JsonPathMatchers.isJsonFile; +import static com.jayway.jsonpath.matchers.helpers.ResourceHelpers.resourceAsFile; +import static com.jayway.jsonpath.matchers.helpers.TestingMatchers.*; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertThat; + +public class IsJsonFileTest { + private static final File BOOKS_JSON = resourceAsFile("books.json"); + private static final File INVALID_JSON = resourceAsFile("invalid.json"); + + @BeforeClass + public static void setupStrictJsonParsing() { + Configuration.setDefaults(new StrictParsingConfiguration()); + } + + @AfterClass + public static void setupDefaultJsonParsing() { + Configuration.setDefaults(null); + } + + @Test + public void shouldMatchJsonFileEvaluatedToTrue() { + assertThat(BOOKS_JSON, isJsonFile(withPathEvaluatedTo(true))); + } + + @Test + public void shouldNotMatchJsonFileEvaluatedToFalse() { + assertThat(BOOKS_JSON, not(isJsonFile(withPathEvaluatedTo(false)))); + } + + @Test + public void shouldNotMatchInvalidJsonFile() { + assertThat(INVALID_JSON, not(isJsonFile(withPathEvaluatedTo(true)))); + assertThat(INVALID_JSON, not(isJsonFile(withPathEvaluatedTo(false)))); + } + + @Test + public void shouldBeDescriptive() { + Matcher matcher = isJsonFile(withPathEvaluatedTo(true)); + Description description = new StringDescription(); + matcher.describeTo(description); + assertThat(description.toString(), startsWith("is json")); + assertThat(description.toString(), containsString(MATCH_TRUE_TEXT)); + } + + @Test + public void shouldDescribeMismatchOfValidJson() { + Matcher matcher = isJsonFile(withPathEvaluatedTo(true)); + Description description = new StringDescription(); + matcher.describeMismatch(BOOKS_JSON, description); + assertThat(description.toString(), containsString(MISMATCHED_TEXT)); + } + + @Test + public void shouldDescribeMismatchOfInvalidJson() { + Matcher matcher = isJsonFile(withPathEvaluatedTo(true)); + Description description = new StringDescription(); + matcher.describeMismatch(INVALID_JSON, description); + assertThat(description.toString(), containsString("invalid.json")); + assertThat(description.toString(), containsString("invalid-json")); + } +} diff --git a/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/IsJsonStringTest.java b/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/IsJsonStringTest.java new file mode 100644 index 00000000..272eb10b --- /dev/null +++ b/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/IsJsonStringTest.java @@ -0,0 +1,71 @@ +package com.jayway.jsonpath.matchers; + +import com.jayway.jsonpath.Configuration; +import com.jayway.jsonpath.matchers.helpers.StrictParsingConfiguration; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.StringDescription; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import static com.jayway.jsonpath.matchers.JsonPathMatchers.isJsonString; +import static com.jayway.jsonpath.matchers.helpers.ResourceHelpers.resource; +import static com.jayway.jsonpath.matchers.helpers.TestingMatchers.*; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertThat; + +public class IsJsonStringTest { + private static final String BOOKS_JSON = resource("books.json"); + + @BeforeClass + public static void setupStrictJsonParsing() { + Configuration.setDefaults(new StrictParsingConfiguration()); + } + + @AfterClass + public static void setupDefaultJsonParsing() { + Configuration.setDefaults(null); + } + + @Test + public void shouldMatchJsonStringEvaluatedToTrue() { + assertThat(BOOKS_JSON, isJsonString(withPathEvaluatedTo(true))); + } + + @Test + public void shouldNotMatchJsonStringEvaluatedToFalse() { + assertThat(BOOKS_JSON, not(isJsonString(withPathEvaluatedTo(false)))); + } + + @Test + public void shouldNotMatchInvalidJsonString() { + assertThat("invalid-json", not(isJsonString(withPathEvaluatedTo(true)))); + assertThat("invalid-json", not(isJsonString(withPathEvaluatedTo(false)))); + } + + @Test + public void shouldBeDescriptive() { + Matcher matcher = isJsonString(withPathEvaluatedTo(true)); + Description description = new StringDescription(); + matcher.describeTo(description); + assertThat(description.toString(), startsWith("is json")); + assertThat(description.toString(), containsString(MATCH_TRUE_TEXT)); + } + + @Test + public void shouldDescribeMismatchOfValidJson() { + Matcher matcher = isJsonString(withPathEvaluatedTo(true)); + Description description = new StringDescription(); + matcher.describeMismatch(BOOKS_JSON, description); + assertThat(description.toString(), containsString(MISMATCHED_TEXT)); + } + + @Test + public void shouldDescribeMismatchOfInvalidJson() { + Matcher matcher = isJsonString(withPathEvaluatedTo(true)); + Description description = new StringDescription(); + matcher.describeMismatch("invalid-json", description); + assertThat(description.toString(), containsString("\"invalid-json\"")); + } +} diff --git a/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/IsJsonTest.java b/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/IsJsonTest.java new file mode 100644 index 00000000..0d08aa02 --- /dev/null +++ b/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/IsJsonTest.java @@ -0,0 +1,97 @@ +package com.jayway.jsonpath.matchers; + +import com.jayway.jsonpath.Configuration; +import com.jayway.jsonpath.matchers.helpers.StrictParsingConfiguration; +import com.jayway.jsonpath.matchers.helpers.TestingMatchers; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.StringDescription; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; + +import static com.jayway.jsonpath.matchers.JsonPathMatchers.isJson; +import static com.jayway.jsonpath.matchers.helpers.ResourceHelpers.resource; +import static com.jayway.jsonpath.matchers.helpers.ResourceHelpers.resourceAsFile; +import static com.jayway.jsonpath.matchers.helpers.TestingMatchers.withPathEvaluatedTo; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertThat; + +public class IsJsonTest { + private static final String BOOKS_JSON_STRING = resource("books.json"); + private static final File BOOKS_JSON_FILE = resourceAsFile("books.json"); + + @BeforeClass + public static void setupStrictJsonParsing() { + Configuration.setDefaults(new StrictParsingConfiguration()); + } + + @AfterClass + public static void setupDefaultJsonParsing() { + Configuration.setDefaults(null); + } + + @Test + public void shouldMatchJsonObjectEvaluatedToTrue() { + Object parsedJson = parseJson(BOOKS_JSON_STRING); + assertThat(parsedJson, isJson(withPathEvaluatedTo(true))); + } + + @Test + public void shouldNotMatchJsonObjectEvaluatedToFalse() { + Object parsedJson = parseJson(BOOKS_JSON_STRING); + assertThat(parsedJson, not(isJson(withPathEvaluatedTo(false)))); + } + + @Test + public void shouldMatchJsonStringEvaluatedToTrue() { + assertThat(BOOKS_JSON_STRING, isJson(withPathEvaluatedTo(true))); + } + + @Test + public void shouldNotMatchJsonStringEvaluatedToFalse() { + assertThat(BOOKS_JSON_STRING, not(isJson(withPathEvaluatedTo(false)))); + } + + @Test + public void shouldMatchJsonFileEvaluatedToTrue() { + assertThat(BOOKS_JSON_FILE, isJson(withPathEvaluatedTo(true))); + } + + @Test + public void shouldNotMatchJsonFileEvaluatedToFalse() { + assertThat(BOOKS_JSON_FILE, not(isJson(withPathEvaluatedTo(false)))); + } + + @Test + public void shouldBeDescriptive() { + Matcher matcher = isJson(withPathEvaluatedTo(true)); + Description description = new StringDescription(); + matcher.describeTo(description); + assertThat(description.toString(), startsWith("is json")); + assertThat(description.toString(), containsString(TestingMatchers.MATCH_TRUE_TEXT)); + } + + @Test + public void shouldDescribeMismatchOfValidJson() { + Matcher matcher = isJson(withPathEvaluatedTo(true)); + Description description = new StringDescription(); + matcher.describeMismatch(BOOKS_JSON_STRING, description); + assertThat(description.toString(), containsString(TestingMatchers.MISMATCHED_TEXT)); + } + + @Test + public void shouldDescribeMismatchOfInvalidJson() { + Matcher matcher = isJson(withPathEvaluatedTo(true)); + Description description = new StringDescription(); + matcher.describeMismatch("invalid-json", description); + assertThat(description.toString(), containsString("\"invalid-json\"")); + } + + private static Object parseJson(String json) { + return Configuration.defaultConfiguration().jsonProvider().parse(json); + } + +} diff --git a/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/JsonPathMatchersTest.java b/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/JsonPathMatchersTest.java new file mode 100644 index 00000000..f52edafd --- /dev/null +++ b/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/JsonPathMatchersTest.java @@ -0,0 +1,144 @@ +package com.jayway.jsonpath.matchers; + +import com.jayway.jsonpath.Configuration; +import com.jayway.jsonpath.matchers.helpers.StrictParsingConfiguration; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; +import java.nio.file.Paths; +import java.util.Collection; + +import static com.jayway.jsonpath.matchers.JsonPathMatchers.*; +import static com.jayway.jsonpath.matchers.helpers.ResourceHelpers.resource; +import static com.jayway.jsonpath.matchers.helpers.ResourceHelpers.resourceAsFile; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertThat; + +public class JsonPathMatchersTest { + + private static final String VALID_JSON = resource("example.json"); + private static final String BOOKS_JSON = resource("books.json"); + private static final String INVALID_JSON = "{ invalid-json }"; + private static final File BOOKS_JSON_FILE = resourceAsFile("books.json"); + + @BeforeClass + public static void setupStrictJsonParsing() { + // NOTE: Evaluation depends on the default configuration of JsonPath + Configuration.setDefaults(new StrictParsingConfiguration()); + } + + @AfterClass + public static void setupDefaultJsonParsing() { + Configuration.setDefaults(null); + } + + @Test + public void shouldMatchValidJson() { + assertThat(VALID_JSON, isJson()); + assertThat(BOOKS_JSON, isJson()); + } + + @Test + public void shouldNotMatchInvalidJson() { + assertThat(INVALID_JSON, not(isJson())); + assertThat(new Object(), not(isJson())); + assertThat("{}", not(isJson())); + assertThat(null, not(isJson())); + } + + @Test + public void shouldMatchExistingJsonPath() { + assertThat(BOOKS_JSON, hasJsonPath("$.store.name")); + assertThat(BOOKS_JSON, hasJsonPath("$.store.book[2].title")); + assertThat(BOOKS_JSON, hasJsonPath("$.store.book[*].author")); + } + + @Test + public void shouldMatchExistingJsonPathAlternative() { + assertThat(BOOKS_JSON, isJson(withJsonPath("$.store.name"))); + assertThat(BOOKS_JSON, isJson(withJsonPath("$.store.book[2].title"))); + assertThat(BOOKS_JSON, isJson(withJsonPath("$.store.book[*].author"))); + } + + @Test + public void shouldNotMatchInvalidJsonWithPath() { + assertThat(INVALID_JSON, not(hasJsonPath("$.path"))); + assertThat(new Object(), not(hasJsonPath("$.path"))); + assertThat("{}", not(hasJsonPath("$.path"))); + assertThat(null, not(hasJsonPath("$.path"))); + } + + @Test + public void shouldNotMatchInvalidJsonWithPathAndValue() { + assertThat(INVALID_JSON, not(hasJsonPath("$.path", anything()))); + assertThat(new Object(), not(hasJsonPath("$.path", anything()))); + assertThat(null, not(hasJsonPath("$.message", anything()))); + } + + @Test + public void shouldNotMatchNonExistingJsonPath() { + assertThat(BOOKS_JSON, not(hasJsonPath("$.not_there"))); + assertThat(BOOKS_JSON, not(hasJsonPath("$.store.book[5].title"))); + assertThat(BOOKS_JSON, not(hasJsonPath("$.store.book[*].not_there"))); + } + + @Test + public void shouldNotMatchNonExistingJsonPathAlternative() { + assertThat(BOOKS_JSON, not(isJson(withJsonPath("$.not_there")))); + assertThat(BOOKS_JSON, not(isJson(withJsonPath("$.store.book[5].title")))); + assertThat(BOOKS_JSON, not(isJson(withJsonPath("$.store.book[*].not_there")))); + } + + @Test + public void shouldMatchJsonPathWithStringValue() { + assertThat(BOOKS_JSON, hasJsonPath("$.store.name", equalTo("Little Shop"))); + assertThat(BOOKS_JSON, hasJsonPath("$.store.book[2].title", equalTo("Moby Dick"))); + } + + @Test + public void shouldMatchJsonPathWithIntegerValue() { + assertThat(BOOKS_JSON, hasJsonPath("$.expensive", equalTo(10))); + } + + @Test + public void shouldMatchJsonPathWithDoubleValue() { + assertThat(BOOKS_JSON, hasJsonPath("$.store.bicycle.price", equalTo(19.95))); + } + + @Test + public void shouldMatchJsonPathWithCollectionValue() { + assertThat(BOOKS_JSON, hasJsonPath("$.store.book[*].author", instanceOf(Collection.class))); + assertThat(BOOKS_JSON, hasJsonPath("$.store.book[*].author", hasSize(4))); + assertThat(BOOKS_JSON, hasJsonPath("$.store.book[*].author", hasItem("Evelyn Waugh"))); + assertThat(BOOKS_JSON, hasJsonPath("$..book[2].title", hasItem("Moby Dick"))); + } + + @Test + public void shouldMatchJsonPathOnFile() { + assertThat(BOOKS_JSON_FILE, hasJsonPath("$.store.name", equalTo("Little Shop"))); + } + + @Test + public void shouldNotMatchJsonPathOnNonExistingFile() { + File nonExistingFile = Paths.get("missing-file").toFile(); + assertThat(nonExistingFile, not(hasJsonPath("$..*", anything()))); + } + + @Test + public void shouldMatchJsonPathOnParsedJsonObject() { + Object json = Configuration.defaultConfiguration().jsonProvider().parse(BOOKS_JSON); + assertThat(json, hasJsonPath("$.store.name", equalTo("Little Shop"))); + } + + @Test + public void shouldMatchMissingJsonPath() { + assertThat(BOOKS_JSON, hasNoJsonPath("$.not_there")); + } + + @Test + public void shouldNotMatchExistingJsonPath() { + assertThat(BOOKS_JSON, not(hasNoJsonPath("$.store.name"))); + } +} diff --git a/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/WithJsonPathTest.java b/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/WithJsonPathTest.java new file mode 100644 index 00000000..6b26cd0c --- /dev/null +++ b/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/WithJsonPathTest.java @@ -0,0 +1,143 @@ +package com.jayway.jsonpath.matchers; + +import com.jayway.jsonpath.InvalidPathException; +import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.ReadContext; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.StringDescription; +import org.junit.Test; + +import java.util.Collection; + +import static com.jayway.jsonpath.JsonPath.compile; +import static com.jayway.jsonpath.matchers.JsonPathMatchers.withJsonPath; +import static com.jayway.jsonpath.matchers.JsonPathMatchers.withoutJsonPath; +import static com.jayway.jsonpath.matchers.helpers.ResourceHelpers.resource; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertThat; + +public class WithJsonPathTest { + private static final ReadContext BOOKS_JSON = JsonPath.parse(resource("books.json")); + + @Test + public void shouldMatchExistingCompiledJsonPath() { + assertThat(BOOKS_JSON, withJsonPath(compile("$.expensive"))); + assertThat(BOOKS_JSON, withJsonPath(compile("$.store.bicycle"))); + assertThat(BOOKS_JSON, withJsonPath(compile("$.store.book[2].title"))); + assertThat(BOOKS_JSON, withJsonPath(compile("$.store.book[*].author"))); + } + + @Test + public void shouldMatchExistingStringJsonPath() { + assertThat(BOOKS_JSON, withJsonPath("$.expensive")); + assertThat(BOOKS_JSON, withJsonPath("$.store.bicycle")); + assertThat(BOOKS_JSON, withJsonPath("$.store.book[2].title")); + assertThat(BOOKS_JSON, withJsonPath("$.store.book[*].author")); + } + + @Test + public void shouldNotMatchNonExistingJsonPath() { + assertThat(BOOKS_JSON, not(withJsonPath(compile("$.not_there")))); + assertThat(BOOKS_JSON, not(withJsonPath(compile("$.store.book[5].title")))); + assertThat(BOOKS_JSON, not(withJsonPath(compile("$.store.book[*].not_there")))); + } + + @Test + public void shouldNotMatchNonExistingStringJsonPath() { + assertThat(BOOKS_JSON, not(withJsonPath("$.not_there"))); + assertThat(BOOKS_JSON, not(withJsonPath("$.store.book[5].title"))); + assertThat(BOOKS_JSON, not(withJsonPath("$.store.book[*].not_there"))); + } + + @Test + public void shouldMatchNonExistingJsonPath() { + assertThat(BOOKS_JSON, withoutJsonPath(compile("$.not_there"))); + } + + @Test + public void shouldMatchNonExistingStringJsonPath() { + assertThat(BOOKS_JSON, withoutJsonPath("$.not_there")); + } + + @Test + public void shouldNotMatchExistingCompiledJsonPath() { + assertThat(BOOKS_JSON, not(withoutJsonPath(compile("$.store.name")))); + } + + @Test + public void shouldNotMatchExistingStringJsonPath() { + assertThat(BOOKS_JSON, not(withoutJsonPath("$.store.name"))); + } + + @Test + public void shouldMatchJsonPathEvaluatedToStringValue() { + assertThat(BOOKS_JSON, withJsonPath(compile("$.store.bicycle.color"), equalTo("red"))); + assertThat(BOOKS_JSON, withJsonPath(compile("$.store.book[2].title"), equalTo("Moby Dick"))); + assertThat(BOOKS_JSON, withJsonPath("$.store.name", equalTo("Little Shop"))); + assertThat(BOOKS_JSON, withJsonPath("$.store.book[2].title", equalTo("Moby Dick"))); + } + + @Test + public void shouldMatchJsonPathEvaluatedToIntegerValue() { + assertThat(BOOKS_JSON, withJsonPath(compile("$.expensive"), equalTo(10))); + assertThat(BOOKS_JSON, withJsonPath("$.expensive", equalTo(10))); + } + + @Test + public void shouldMatchJsonPathEvaluatedToDoubleValue() { + assertThat(BOOKS_JSON, withJsonPath(compile("$.store.bicycle.price"), equalTo(19.95))); + assertThat(BOOKS_JSON, withJsonPath("$.store.bicycle.price", equalTo(19.95))); + } + + @Test + public void shouldMatchJsonPathEvaluatedToCollectionValue() { + assertThat(BOOKS_JSON, withJsonPath(compile("$.store.book[*].author"), instanceOf(Collection.class))); + assertThat(BOOKS_JSON, withJsonPath(compile("$.store.book[*].author"), hasSize(4))); + assertThat(BOOKS_JSON, withJsonPath(compile("$.store.book[*].author"), hasItem("Evelyn Waugh"))); + assertThat(BOOKS_JSON, withJsonPath(compile("$..book[2].title"), hasItem("Moby Dick"))); + assertThat(BOOKS_JSON, withJsonPath("$.store.book[*].author", instanceOf(Collection.class))); + assertThat(BOOKS_JSON, withJsonPath("$.store.book[*].author", hasSize(4))); + assertThat(BOOKS_JSON, withJsonPath("$.store.book[*].author", hasItem("Evelyn Waugh"))); + assertThat(BOOKS_JSON, withJsonPath("$..book[2].title", hasItem("Moby Dick"))); + } + + @Test(expected = InvalidPathException.class) + public void shouldFailOnInvalidJsonPath() { + withJsonPath("$[}"); + } + + @Test + public void shouldNotMatchOnInvalidJson() { + ReadContext invalidJson = JsonPath.parse("invalid-json"); + assertThat(invalidJson, not(withJsonPath("$.expensive", equalTo(10)))); + } + + @Test + public void shouldBeDescriptive() { + Matcher matcher = withJsonPath("path", equalTo(2)); + Description description = new StringDescription(); + matcher.describeTo(description); + assertThat(description.toString(), containsString("path")); + assertThat(description.toString(), containsString("<2>")); + } + + @Test + public void shouldDescribeMismatchOfEvaluation() { + Matcher matcher = withJsonPath("expensive", equalTo(3)); + Description description = new StringDescription(); + matcher.describeMismatch(BOOKS_JSON, description); + assertThat(description.toString(), containsString("expensive")); + assertThat(description.toString(), containsString("<10>")); + } + + @Test + public void shouldDescribeMismatchOfPathNotFound() { + Matcher matcher = withJsonPath("not-here", equalTo(3)); + Description description = new StringDescription(); + matcher.describeMismatch(BOOKS_JSON, description); + assertThat(description.toString(), containsString("not-here")); + assertThat(description.toString(), containsString("was not found")); + } + +} diff --git a/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/helpers/ResourceHelpers.java b/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/helpers/ResourceHelpers.java new file mode 100644 index 00000000..c2c8724a --- /dev/null +++ b/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/helpers/ResourceHelpers.java @@ -0,0 +1,35 @@ +package com.jayway.jsonpath.matchers.helpers; + +import org.apache.commons.io.IOUtils; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static java.lang.ClassLoader.getSystemResource; +import static java.lang.ClassLoader.getSystemResourceAsStream; + +public class ResourceHelpers { + public static String resource(String resource) { + try { + return IOUtils.toString(getSystemResourceAsStream(resource)); + } catch (IOException e) { + throw new AssertionError("Resource not found", e); + } + } + + public static File resourceAsFile(String resource) { + try { + URL systemResource = getSystemResource(resource); + URI uri = systemResource.toURI(); + Path path = Paths.get(uri); + return path.toFile(); + } catch (URISyntaxException e) { + throw new AssertionError("URI syntax error", e); + } + } +} diff --git a/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/helpers/StrictParsingConfiguration.java b/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/helpers/StrictParsingConfiguration.java new file mode 100644 index 00000000..bfec47b2 --- /dev/null +++ b/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/helpers/StrictParsingConfiguration.java @@ -0,0 +1,30 @@ +package com.jayway.jsonpath.matchers.helpers; + +import com.jayway.jsonpath.Configuration; +import com.jayway.jsonpath.Option; +import com.jayway.jsonpath.spi.json.JsonProvider; +import com.jayway.jsonpath.spi.json.JsonSmartJsonProvider; +import com.jayway.jsonpath.spi.mapper.JsonSmartMappingProvider; +import com.jayway.jsonpath.spi.mapper.MappingProvider; +import net.minidev.json.parser.JSONParser; + +import java.util.EnumSet; +import java.util.Set; + +public class StrictParsingConfiguration implements Configuration.Defaults { + + private final JsonProvider jsonProvider = new JsonSmartJsonProvider(JSONParser.MODE_STRICTEST); + private final MappingProvider mappingProvider = new JsonSmartMappingProvider(); + + public JsonProvider jsonProvider() { + return jsonProvider; + } + + public MappingProvider mappingProvider() { + return mappingProvider; + } + + public Set