From 1eed503f3108027a71ce0523066402625bd974d3 Mon Sep 17 00:00:00 2001 From: Lukas Krecan Date: Thu, 28 Nov 2024 20:47:28 +0100 Subject: [PATCH] #859 Support for JsonPath in Spring --- README.md | 24 +-------- .../spring/AbstractSpringMatcher.java | 12 +++-- .../spring/AbstractSpringMatchers.java | 51 +++++++++++++------ .../spring/JsonUnitRequestMatchers.java | 25 +++++---- .../spring/JsonUnitResultMatchers.java | 25 +++++---- .../spring/WebTestClientJsonMatcher.java | 24 +++++---- .../spring/testit/AssertJMockMvcTest.java | 4 +- .../jsonunit/spring/testit/ClientTest.java | 12 +++++ .../jsonunit/spring/testit/MockMvcTest.java | 5 ++ .../spring/testit/WebTestClientTest.java | 5 ++ 10 files changed, 113 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index 4b62491e..ec5bd49e 100644 --- a/README.md +++ b/README.md @@ -380,7 +380,7 @@ See the [tests](https://github.com/lukas-krecan/JsonUnit/blob/master/json-unit-k JsonUnit support all this features regardless of API you use. ## JsonPath support -You can use JsonPath navigation together with JsonUnit. It has native support in AssertJ integration, so you can do something like this: +You can use JsonPath navigation together with JsonUnit. It has native support in AssertJ and Spring integration, so you can do something like this: ```java // AssertJ style @@ -397,28 +397,6 @@ assertThatJson(json) )); ``` -For the other API styles you have to first import JsonPath support module -```xml - - net.javacrumbs.json-unit - json-unit-json-path - 4.0.0 - test - -``` - -and then use instead of actual value - -```xml -import static net.javacrumbs.jsonunit.jsonpath.JsonPathAdapter.inPath; - -... -// Fluent assertions -assertThatJson(inPath(json, "$.store.book[*].author")) - .when(Option.IGNORING_ARRAY_ORDER) - .isEqualTo("['J. R. R. Tolkien', 'Nigel Rees', 'Evelyn Waugh', 'Herman Melville']"); -``` - ## Ignoring values Sometimes you need to ignore certain values when comparing. It is possible to use `${json-unit.ignore}` or `#{json-unit.ignore}` placeholder like this diff --git a/json-unit-spring/src/main/java/net/javacrumbs/jsonunit/spring/AbstractSpringMatcher.java b/json-unit-spring/src/main/java/net/javacrumbs/jsonunit/spring/AbstractSpringMatcher.java index b78e1c19..ae7e1f6f 100644 --- a/json-unit-spring/src/main/java/net/javacrumbs/jsonunit/spring/AbstractSpringMatcher.java +++ b/json-unit-spring/src/main/java/net/javacrumbs/jsonunit/spring/AbstractSpringMatcher.java @@ -15,7 +15,8 @@ */ package net.javacrumbs.jsonunit.spring; -import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; import net.javacrumbs.jsonunit.core.Configuration; import net.javacrumbs.jsonunit.core.internal.Path; import net.javacrumbs.jsonunit.core.internal.matchers.InternalMatcher; @@ -24,18 +25,21 @@ abstract class AbstractSpringMatcher { private final Path path; private final Configuration configuration; - private final BiConsumer matcher; + private final Consumer matcher; + private final Function jsonTransformer; AbstractSpringMatcher( @NotNull Path path, @NotNull Configuration configuration, - @NotNull BiConsumer matcher) { + @NotNull Consumer matcher, + @NotNull Function jsonTransformer) { this.path = path; this.configuration = configuration; this.matcher = matcher; + this.jsonTransformer = jsonTransformer; } void doMatch(Object actual) { - matcher.accept(actual, new InternalMatcher(actual, path, "", configuration)); + matcher.accept(new InternalMatcher(jsonTransformer.apply(actual), path, "", configuration)); } } diff --git a/json-unit-spring/src/main/java/net/javacrumbs/jsonunit/spring/AbstractSpringMatchers.java b/json-unit-spring/src/main/java/net/javacrumbs/jsonunit/spring/AbstractSpringMatchers.java index 3a062ab3..1da87f10 100644 --- a/json-unit-spring/src/main/java/net/javacrumbs/jsonunit/spring/AbstractSpringMatchers.java +++ b/json-unit-spring/src/main/java/net/javacrumbs/jsonunit/spring/AbstractSpringMatchers.java @@ -16,13 +16,15 @@ package net.javacrumbs.jsonunit.spring; import java.math.BigDecimal; -import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; import net.javacrumbs.jsonunit.core.Configuration; import net.javacrumbs.jsonunit.core.ConfigurationWhen; import net.javacrumbs.jsonunit.core.Option; import net.javacrumbs.jsonunit.core.internal.Path; import net.javacrumbs.jsonunit.core.internal.matchers.InternalMatcher; import net.javacrumbs.jsonunit.core.listener.DifferenceListener; +import net.javacrumbs.jsonunit.jsonpath.JsonPathAdapter; import org.hamcrest.Matcher; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -35,17 +37,27 @@ abstract class AbstractSpringMatchers { final Path path; final Configuration configuration; + final Function jsonTransformer; - AbstractSpringMatchers(@NotNull Path path, @NotNull Configuration configuration) { + AbstractSpringMatchers( + @NotNull Path path, @NotNull Configuration configuration, Function jsonTransformer) { this.path = path; this.configuration = configuration; + this.jsonTransformer = jsonTransformer; } @NotNull - abstract MATCHER matcher(@NotNull BiConsumer matcher); + abstract MATCHER matcher(@NotNull Consumer matcher); @NotNull - abstract ME matchers(@NotNull Path path, @NotNull Configuration configuration); + abstract ME matchers( + @NotNull Path path, + @NotNull Configuration configuration, + @NotNull Function jsonTransformer); + + protected ME matchers(@NotNull Path path, @NotNull Configuration configuration) { + return matchers(path, configuration, jsonTransformer); + } /** * Creates a matcher object that only compares given node. @@ -55,7 +67,6 @@ abstract class AbstractSpringMatchers { * this.mockMvc.perform(get("/sample").accept(MediaType.APPLICATION_JSON)).andExpect(json().node("root.test[0]").isEqualTo("1")); * * - * @param newPath * @return object comparing only node given by path. */ @NotNull @@ -63,6 +74,14 @@ public ME node(String newPath) { return matchers(path.copy(newPath), configuration); } + /** + * Uses JsonPath to extract values from the actual value. + */ + @NotNull + public ME inPath(String path) { + return matchers(this.path, configuration, json -> JsonPathAdapter.inPath(json, path)); + } + /** * Sets the placeholder that can be used to ignore values. * The default value is ${json-unit.ignore} @@ -143,7 +162,7 @@ public ME when( */ @NotNull public MATCHER isEqualTo(@Nullable Object expected) { - return matcher((actual, ctx) -> ctx.isEqualTo(expected)); + return matcher(ctx -> ctx.isEqualTo(expected)); } /** @@ -152,7 +171,7 @@ public MATCHER isEqualTo(@Nullable Object expected) { */ @NotNull public MATCHER isStringEqualTo(@Nullable final String expected) { - return matcher((actual, ctx) -> ctx.isStringEqualTo(expected)); + return matcher(ctx -> ctx.isStringEqualTo(expected)); } /** @@ -161,7 +180,7 @@ public MATCHER isStringEqualTo(@Nullable final String expected) { */ @NotNull public MATCHER isNotEqualTo(@Nullable Object expected) { - return matcher((actual, ctx) -> ctx.isNotEqualTo(expected)); + return matcher(ctx -> ctx.isNotEqualTo(expected)); } /** @@ -169,7 +188,7 @@ public MATCHER isNotEqualTo(@Nullable Object expected) { */ @NotNull public MATCHER isAbsent() { - return matcher((actual, ctx) -> ctx.isAbsent()); + return matcher(ctx -> ctx.isAbsent()); } /** @@ -177,7 +196,7 @@ public MATCHER isAbsent() { */ @NotNull public MATCHER isPresent() { - return matcher((actual, ctx) -> ctx.isPresent()); + return matcher(InternalMatcher::isPresent); } /** @@ -185,7 +204,7 @@ public MATCHER isPresent() { */ @NotNull public MATCHER isArray() { - return matcher((actual, ctx) -> ctx.isArray()); + return matcher(InternalMatcher::isArray); } /** @@ -193,7 +212,7 @@ public MATCHER isArray() { */ @NotNull public MATCHER isObject() { - return matcher((actual, ctx) -> ctx.isObject()); + return matcher(InternalMatcher::isObject); } /** @@ -201,7 +220,7 @@ public MATCHER isObject() { */ @NotNull public MATCHER isString() { - return matcher((actual, ctx) -> ctx.isString()); + return matcher(InternalMatcher::isString); } /** @@ -209,7 +228,7 @@ public MATCHER isString() { */ @NotNull public MATCHER isNull() { - return matcher((actual, ctx) -> ctx.isNull()); + return matcher(InternalMatcher::isNull); } /** @@ -217,7 +236,7 @@ public MATCHER isNull() { */ @NotNull public MATCHER isNotNull() { - return matcher((actual, ctx) -> ctx.isNotNull()); + return matcher(InternalMatcher::isNotNull); } /** @@ -234,7 +253,7 @@ public MATCHER isNotNull() { */ @NotNull public MATCHER matches(@NotNull final Matcher matcher) { - return matcher((actual, ctx) -> ctx.matches(matcher)); + return matcher(ctx -> ctx.matches(matcher)); } /** diff --git a/json-unit-spring/src/main/java/net/javacrumbs/jsonunit/spring/JsonUnitRequestMatchers.java b/json-unit-spring/src/main/java/net/javacrumbs/jsonunit/spring/JsonUnitRequestMatchers.java index 5314ac0d..e8356304 100644 --- a/json-unit-spring/src/main/java/net/javacrumbs/jsonunit/spring/JsonUnitRequestMatchers.java +++ b/json-unit-spring/src/main/java/net/javacrumbs/jsonunit/spring/JsonUnitRequestMatchers.java @@ -15,7 +15,8 @@ */ package net.javacrumbs.jsonunit.spring; -import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; import net.javacrumbs.jsonunit.core.Configuration; import net.javacrumbs.jsonunit.core.internal.Path; import net.javacrumbs.jsonunit.core.internal.matchers.InternalMatcher; @@ -37,20 +38,23 @@ */ public class JsonUnitRequestMatchers extends AbstractSpringMatchers { - private JsonUnitRequestMatchers(Path path, Configuration configuration) { - super(path, configuration); + private JsonUnitRequestMatchers(Path path, Configuration configuration, Function jsonTransformer) { + super(path, configuration, jsonTransformer); } @NotNull @Override - RequestMatcher matcher(@NotNull BiConsumer matcher) { - return new JsonRequestMatcher(path, configuration, matcher); + RequestMatcher matcher(@NotNull Consumer matcher) { + return new JsonRequestMatcher(path, configuration, matcher, jsonTransformer); } @Override @NotNull - JsonUnitRequestMatchers matchers(@NotNull Path path, @NotNull Configuration configuration) { - return new JsonUnitRequestMatchers(path, configuration); + JsonUnitRequestMatchers matchers( + @NotNull Path path, + @NotNull Configuration configuration, + @NotNull Function jsonTransformer) { + return new JsonUnitRequestMatchers(path, configuration, jsonTransformer); } /** @@ -58,15 +62,16 @@ JsonUnitRequestMatchers matchers(@NotNull Path path, @NotNull Configuration conf */ @NotNull public static JsonUnitRequestMatchers json() { - return new JsonUnitRequestMatchers(Path.root(), Configuration.empty()); + return new JsonUnitRequestMatchers(Path.root(), Configuration.empty(), Function.identity()); } private static class JsonRequestMatcher extends AbstractSpringMatcher implements RequestMatcher { private JsonRequestMatcher( @NotNull Path path, @NotNull Configuration configuration, - @NotNull BiConsumer matcher) { - super(path, configuration, matcher); + @NotNull Consumer matcher, + @NotNull Function jsonTransformer) { + super(path, configuration, matcher, jsonTransformer); } @Override diff --git a/json-unit-spring/src/main/java/net/javacrumbs/jsonunit/spring/JsonUnitResultMatchers.java b/json-unit-spring/src/main/java/net/javacrumbs/jsonunit/spring/JsonUnitResultMatchers.java index e01a52c9..380fedcb 100644 --- a/json-unit-spring/src/main/java/net/javacrumbs/jsonunit/spring/JsonUnitResultMatchers.java +++ b/json-unit-spring/src/main/java/net/javacrumbs/jsonunit/spring/JsonUnitResultMatchers.java @@ -17,7 +17,8 @@ import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; -import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; import net.javacrumbs.jsonunit.core.Configuration; import net.javacrumbs.jsonunit.core.internal.Path; import net.javacrumbs.jsonunit.core.internal.matchers.InternalMatcher; @@ -36,35 +37,39 @@ * */ public class JsonUnitResultMatchers extends AbstractSpringMatchers { - private JsonUnitResultMatchers(Path path, Configuration configuration) { - super(path, configuration); + private JsonUnitResultMatchers(Path path, Configuration configuration, Function jsonTransformer) { + super(path, configuration, jsonTransformer); } /** * Creates JsonUnitResultMatchers to be used for JSON assertions. */ public static JsonUnitResultMatchers json() { - return new JsonUnitResultMatchers(Path.root(), Configuration.empty()); + return new JsonUnitResultMatchers(Path.root(), Configuration.empty(), Function.identity()); } @Override @NotNull - ResultMatcher matcher(@NotNull BiConsumer matcher) { - return new JsonResultMatcher(path, configuration, matcher); + ResultMatcher matcher(@NotNull Consumer matcher) { + return new JsonResultMatcher(path, configuration, matcher, jsonTransformer); } @Override @NotNull - JsonUnitResultMatchers matchers(@NotNull Path path, @NotNull Configuration configuration) { - return new JsonUnitResultMatchers(path, configuration); + JsonUnitResultMatchers matchers( + @NotNull Path path, + @NotNull Configuration configuration, + @NotNull Function jsonTransformer) { + return new JsonUnitResultMatchers(path, configuration, jsonTransformer); } private static class JsonResultMatcher extends AbstractSpringMatcher implements ResultMatcher { private JsonResultMatcher( @NotNull Path path, @NotNull Configuration configuration, - @NotNull BiConsumer matcher) { - super(path, configuration, matcher); + @NotNull Consumer matcher, + @NotNull Function jsonTransformer) { + super(path, configuration, matcher, jsonTransformer); } @Override diff --git a/json-unit-spring/src/main/java/net/javacrumbs/jsonunit/spring/WebTestClientJsonMatcher.java b/json-unit-spring/src/main/java/net/javacrumbs/jsonunit/spring/WebTestClientJsonMatcher.java index dd24b0a9..bcb82a1d 100644 --- a/json-unit-spring/src/main/java/net/javacrumbs/jsonunit/spring/WebTestClientJsonMatcher.java +++ b/json-unit-spring/src/main/java/net/javacrumbs/jsonunit/spring/WebTestClientJsonMatcher.java @@ -2,8 +2,8 @@ import static net.javacrumbs.jsonunit.spring.Utils.getContentAsString; -import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.function.Function; import net.javacrumbs.jsonunit.core.Configuration; import net.javacrumbs.jsonunit.core.internal.Path; import net.javacrumbs.jsonunit.core.internal.matchers.InternalMatcher; @@ -22,24 +22,27 @@ */ public class WebTestClientJsonMatcher extends AbstractSpringMatchers>> { - private WebTestClientJsonMatcher(Path path, Configuration configuration) { - super(path, configuration); + private WebTestClientJsonMatcher(Path path, Configuration configuration, Function jsonTransformer) { + super(path, configuration, jsonTransformer); } public static WebTestClientJsonMatcher json() { - return new WebTestClientJsonMatcher(Path.root(), Configuration.empty()); + return new WebTestClientJsonMatcher(Path.root(), Configuration.empty(), Function.identity()); } @Override @NotNull - Consumer> matcher(@NotNull BiConsumer matcher) { - return new JsonUnitWebTestClientMatcher(path, configuration, matcher); + Consumer> matcher(@NotNull Consumer matcher) { + return new JsonUnitWebTestClientMatcher(path, configuration, matcher, jsonTransformer); } @Override @NotNull - WebTestClientJsonMatcher matchers(@NotNull Path path, @NotNull Configuration configuration) { - return new WebTestClientJsonMatcher(path, configuration); + WebTestClientJsonMatcher matchers( + @NotNull Path path, + @NotNull Configuration configuration, + @NotNull Function jsonTransformer) { + return new WebTestClientJsonMatcher(path, configuration, jsonTransformer); } private static class JsonUnitWebTestClientMatcher extends AbstractSpringMatcher @@ -47,8 +50,9 @@ private static class JsonUnitWebTestClientMatcher extends AbstractSpringMatcher private JsonUnitWebTestClientMatcher( @NotNull Path path, @NotNull Configuration configuration, - @NotNull BiConsumer matcher) { - super(path, configuration, matcher); + @NotNull Consumer matcher, + @NotNull Function jsonTransformer) { + super(path, configuration, matcher, jsonTransformer); } @Override diff --git a/json-unit-spring/src/test/java/net/javacrumbs/jsonunit/spring/testit/AssertJMockMvcTest.java b/json-unit-spring/src/test/java/net/javacrumbs/jsonunit/spring/testit/AssertJMockMvcTest.java index 5a730e31..8743b68a 100644 --- a/json-unit-spring/src/test/java/net/javacrumbs/jsonunit/spring/testit/AssertJMockMvcTest.java +++ b/json-unit-spring/src/test/java/net/javacrumbs/jsonunit/spring/testit/AssertJMockMvcTest.java @@ -7,6 +7,7 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import net.javacrumbs.jsonunit.core.Configuration; +import net.javacrumbs.jsonunit.core.Option; import net.javacrumbs.jsonunit.spring.testit.demo.ExampleController; import org.junit.jupiter.api.Test; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; @@ -24,13 +25,14 @@ void shouldWorkWithMockMvcTester() { } @Test - void shouldUseConvertToJAckson() { + void shouldUseConvertToJackson() { MockMvcTester mvc = MockMvcTester.of(new ExampleController()) .withHttpMessageConverters(singleton(new MappingJackson2HttpMessageConverter())); assertThat(mvc.get().uri("/sample")) .hasStatusOk() .bodyJson() .convertTo(jsonUnitJson()) + .when(Option.IGNORING_ARRAY_ORDER) .inPath("result.array") .isArray() .containsExactly(1, 2, 3); diff --git a/json-unit-spring/src/test/java/net/javacrumbs/jsonunit/spring/testit/ClientTest.java b/json-unit-spring/src/test/java/net/javacrumbs/jsonunit/spring/testit/ClientTest.java index 2a560e3a..c0f0c67f 100644 --- a/json-unit-spring/src/test/java/net/javacrumbs/jsonunit/spring/testit/ClientTest.java +++ b/json-unit-spring/src/test/java/net/javacrumbs/jsonunit/spring/testit/ClientTest.java @@ -74,4 +74,16 @@ void shouldAssertClientComplex() { assertThat(restTemplate.postForEntity(URI, json, String.class).getBody()) .isEqualTo(jsonResponse); } + + @Test + void shouldAssertClientJsonPath() { + mockServer + .expect(requestTo(URI)) + .andExpect(method(HttpMethod.POST)) + .andExpect(json().inPath("$.test").isEqualTo(1)) + .andRespond(withSuccess(jsonResponse, MediaType.APPLICATION_JSON)); + + assertThat(restTemplate.postForEntity(URI, json, String.class).getBody()) + .isEqualTo(jsonResponse); + } } diff --git a/json-unit-spring/src/test/java/net/javacrumbs/jsonunit/spring/testit/MockMvcTest.java b/json-unit-spring/src/test/java/net/javacrumbs/jsonunit/spring/testit/MockMvcTest.java index cfe0ee98..52034732 100644 --- a/json-unit-spring/src/test/java/net/javacrumbs/jsonunit/spring/testit/MockMvcTest.java +++ b/json-unit-spring/src/test/java/net/javacrumbs/jsonunit/spring/testit/MockMvcTest.java @@ -74,6 +74,11 @@ void shouldAllowOptionsOnPath() throws Exception { .isEqualTo(CORRECT_JSON)); } + @Test + void shouldSupportJsonPath() throws Exception { + exec("/sampleProduces").andExpect(json().inPath("$.result.array[1]").isEqualTo(2)); + } + @Test void shouldPassIfEqualsWithIsoEncoding() throws Exception { exec("/sampleIso").andExpect(json().node("result").isEqualTo(ISO_VALUE)); diff --git a/json-unit-spring/src/test/java/net/javacrumbs/jsonunit/spring/testit/WebTestClientTest.java b/json-unit-spring/src/test/java/net/javacrumbs/jsonunit/spring/testit/WebTestClientTest.java index 2de9fa18..30078459 100644 --- a/json-unit-spring/src/test/java/net/javacrumbs/jsonunit/spring/testit/WebTestClientTest.java +++ b/json-unit-spring/src/test/java/net/javacrumbs/jsonunit/spring/testit/WebTestClientTest.java @@ -69,6 +69,11 @@ void shouldPassIfEqualsWithIsoEncoding() { exec("/sampleIso").consumeWith(json().node("result").isEqualTo(ISO_VALUE)); } + @Test + void shouldSupportJsonPath() throws Exception { + exec("/sampleProduces").consumeWith(json().inPath("$.result.array[1]").isEqualTo(2)); + } + @Test void shouldPassIfEquals() { exec().consumeWith(json().isEqualTo(CORRECT_JSON));