diff --git a/spring-test/src/main/java/org/springframework/test/json/AbstractJsonContentAssert.java b/spring-test/src/main/java/org/springframework/test/json/AbstractJsonContentAssert.java index 1f5cda8aa4e6..f2992f1418ce 100644 --- a/spring-test/src/main/java/org/springframework/test/json/AbstractJsonContentAssert.java +++ b/spring-test/src/main/java/org/springframework/test/json/AbstractJsonContentAssert.java @@ -24,8 +24,9 @@ import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.PathNotFoundException; -import org.assertj.core.api.AbstractStringAssert; +import org.assertj.core.api.AbstractObjectAssert; import org.assertj.core.api.AssertProvider; +import org.assertj.core.api.Assertions; import org.assertj.core.error.BasicErrorMessageFactory; import org.assertj.core.internal.Failures; @@ -61,7 +62,7 @@ * @param the type of assertions */ public abstract class AbstractJsonContentAssert> - extends AbstractStringAssert { + extends AbstractObjectAssert { private static final Failures failures = Failures.instance(); @@ -79,16 +80,12 @@ public abstract class AbstractJsonContentAssertPath can be converted to a value object using the given - * {@linkplain GenericHttpMessageConverter JSON message converter}. - * @param json the JSON document to assert - * @param jsonMessageConverter the converter to use + * @param actual the JSON document to assert * @param selfType the implementation type of this assert */ - protected AbstractJsonContentAssert(@Nullable String json, - @Nullable GenericHttpMessageConverter jsonMessageConverter, Class selfType) { - super(json, selfType); - this.jsonMessageConverter = jsonMessageConverter; + protected AbstractJsonContentAssert(@Nullable JsonContent actual, Class selfType) { + super(actual, selfType); + this.jsonMessageConverter = (actual != null ? actual.getJsonMessageConverter() : null); this.jsonLoader = new JsonLoader(null, null); as("JSON content"); } @@ -141,6 +138,19 @@ public SELF doesNotHavePath(String path) { // JsonAssert support + /** + * Verify that the actual value is {@linkplain JsonCompareMode#STRICT strictly} + * equal to the given JSON. The {@code expected} value can contain the JSON + * itself or, if it ends with {@code .json}, the name of a resource to be + * loaded from the classpath. + * @param expected the expected JSON or the name of a resource containing + * the expected JSON + * @see #isEqualTo(CharSequence, JsonCompareMode) + */ + public SELF isEqualTo(@Nullable CharSequence expected) { + return isEqualTo(expected, JsonCompareMode.STRICT); + } + /** * Verify that the actual value is equal to the given JSON. The * {@code expected} value can contain the JSON itself or, if it ends with @@ -257,6 +267,19 @@ public SELF isStrictlyEqualTo(Resource expected) { return isEqualTo(expected, JsonCompareMode.STRICT); } + /** + * Verify that the actual value is {@linkplain JsonCompareMode#STRICT strictly} + * not equal to the given JSON. The {@code expected} value can contain the + * JSON itself or, if it ends with {@code .json}, the name of a resource to + * be loaded from the classpath. + * @param expected the expected JSON or the name of a resource containing + * the expected JSON + * @see #isNotEqualTo(CharSequence, JsonCompareMode) + */ + public SELF isNotEqualTo(@Nullable CharSequence expected) { + return isNotEqualTo(expected, JsonCompareMode.STRICT); + } + /** * Verify that the actual value is not equal to the given JSON. The * {@code expected} value can contain the JSON itself or, if it ends with @@ -399,13 +422,24 @@ public SELF withCharset(@Nullable Charset charset) { return this.myself; } + @Nullable + private String toJsonString() { + return (this.actual != null ? this.actual.getJson() : null); + } + + @SuppressWarnings("NullAway") + private String toNonNullJsonString() { + String jsonString = toJsonString(); + Assertions.assertThat(jsonString).as("JSON content").isNotNull(); + return jsonString; + } private JsonComparison compare(@Nullable CharSequence expectedJson, JsonCompareMode compareMode) { return compare(expectedJson, JsonAssert.comparator(compareMode)); } private JsonComparison compare(@Nullable CharSequence expectedJson, JsonComparator comparator) { - return comparator.compare((expectedJson != null) ? expectedJson.toString() : null, this.actual); + return comparator.compare((expectedJson != null) ? expectedJson.toString() : null, toJsonString()); } private SELF assertIsMatch(JsonComparison result) { @@ -435,16 +469,15 @@ private class JsonPathValue { private final String path; - private final JsonPath jsonPath; - private final String json; + private final JsonPath jsonPath; + JsonPathValue(String path) { Assert.hasText(path, "'path' must not be null or empty"); - isNotNull(); this.path = path; + this.json = toNonNullJsonString(); this.jsonPath = JsonPath.compile(this.path); - this.json = AbstractJsonContentAssert.this.actual; } @Nullable diff --git a/spring-test/src/main/java/org/springframework/test/json/JsonContent.java b/spring-test/src/main/java/org/springframework/test/json/JsonContent.java index c801a5fa5d64..383b2c3c3d24 100644 --- a/spring-test/src/main/java/org/springframework/test/json/JsonContent.java +++ b/spring-test/src/main/java/org/springframework/test/json/JsonContent.java @@ -18,6 +18,7 @@ import org.assertj.core.api.AssertProvider; +import org.springframework.http.converter.GenericHttpMessageConverter; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -34,38 +35,54 @@ public final class JsonContent implements AssertProvider { private final String json; @Nullable - private final Class resourceLoadClass; + private final GenericHttpMessageConverter jsonMessageConverter; /** - * Create a new {@code JsonContent} instance. + * Create a new {@code JsonContent} instance with the message converter to + * use to deserialize content. * @param json the actual JSON content - * @param resourceLoadClass the source class used to load resources + * @param jsonMessageConverter the message converter to use */ - JsonContent(String json, @Nullable Class resourceLoadClass) { + public JsonContent(String json, @Nullable GenericHttpMessageConverter jsonMessageConverter) { Assert.notNull(json, "JSON must not be null"); this.json = json; - this.resourceLoadClass = resourceLoadClass; + this.jsonMessageConverter = jsonMessageConverter; } + /** + * Create a new {@code JsonContent} instance. + * @param json the actual JSON content + */ + public JsonContent(String json) { + this(json, null); + } + /** * Use AssertJ's {@link org.assertj.core.api.Assertions#assertThat assertThat} * instead. */ @Override public JsonContentAssert assertThat() { - return new JsonContentAssert(this.json, null).withResourceLoadClass(this.resourceLoadClass); + return new JsonContentAssert(this); } /** * Return the actual JSON content string. - * @return the JSON content */ public String getJson() { return this.json; } + /** + * Return the message converter to use to deserialize content. + */ + @Nullable + GenericHttpMessageConverter getJsonMessageConverter() { + return this.jsonMessageConverter; + } + @Override public String toString() { return "JsonContent " + this.json; diff --git a/spring-test/src/main/java/org/springframework/test/json/JsonContentAssert.java b/spring-test/src/main/java/org/springframework/test/json/JsonContentAssert.java index de4a35f80f96..9728a762496a 100644 --- a/spring-test/src/main/java/org/springframework/test/json/JsonContentAssert.java +++ b/spring-test/src/main/java/org/springframework/test/json/JsonContentAssert.java @@ -16,7 +16,6 @@ package org.springframework.test.json; -import org.springframework.http.converter.GenericHttpMessageConverter; import org.springframework.lang.Nullable; /** @@ -29,13 +28,10 @@ public class JsonContentAssert extends AbstractJsonContentAssertPath can be converted to a value object using the given - * {@linkplain GenericHttpMessageConverter JSON message converter}. * @param json the JSON document to assert - * @param jsonMessageConverter the converter to use */ - public JsonContentAssert(@Nullable String json, @Nullable GenericHttpMessageConverter jsonMessageConverter) { - super(json, jsonMessageConverter, JsonContentAssert.class); + public JsonContentAssert(@Nullable JsonContent json) { + super(json, JsonContentAssert.class); } } diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/assertj/AbstractMockHttpServletResponseAssert.java b/spring-test/src/main/java/org/springframework/test/web/servlet/assertj/AbstractMockHttpServletResponseAssert.java index 9800b2865db9..1bbfd051d94b 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/assertj/AbstractMockHttpServletResponseAssert.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/assertj/AbstractMockHttpServletResponseAssert.java @@ -27,6 +27,7 @@ import org.springframework.lang.Nullable; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.json.AbstractJsonContentAssert; +import org.springframework.test.json.JsonContent; import org.springframework.test.json.JsonContentAssert; import org.springframework.test.web.UriAssert; @@ -92,7 +93,7 @@ public AbstractStringAssert bodyText() { * */ public AbstractJsonContentAssert bodyJson() { - return new JsonContentAssert(readBody(), this.jsonMessageConverter); + return new JsonContentAssert(new JsonContent(readBody(), this.jsonMessageConverter)); } private String readBody() { diff --git a/spring-test/src/test/java/org/springframework/test/json/AbstractJsonContentAssertTests.java b/spring-test/src/test/java/org/springframework/test/json/AbstractJsonContentAssertTests.java index 37568a6d5850..0c350c41357c 100644 --- a/spring-test/src/test/java/org/springframework/test/json/AbstractJsonContentAssertTests.java +++ b/spring-test/src/test/java/org/springframework/test/json/AbstractJsonContentAssertTests.java @@ -92,6 +92,14 @@ void isNullWhenActualIsNullShouldPass() { assertThat(forJson(null)).isNull(); } + @Test + void satisfiesAllowFurtherAssertions() { + assertThat(forJson(SIMPSONS)).satisfies(content -> { + assertThat(content).extractingPath("$.familyMembers[0].name").isEqualTo("Homer"); + assertThat(content).extractingPath("$.familyMembers[1].name").isEqualTo("Marge"); + }); + } + @Nested class HasPathTests { @@ -831,7 +839,7 @@ private AssertProvider> forJson(@Nullable String js private static class TestJsonContentAssert extends AbstractJsonContentAssert { public TestJsonContentAssert(@Nullable String json, @Nullable GenericHttpMessageConverter jsonMessageConverter) { - super(json, jsonMessageConverter, TestJsonContentAssert.class); + super((json != null ? new JsonContent(json, jsonMessageConverter) : null), TestJsonContentAssert.class); } } diff --git a/spring-test/src/test/java/org/springframework/test/json/JsonContentTests.java b/spring-test/src/test/java/org/springframework/test/json/JsonContentTests.java index 6e4131c46f66..b87e8849b054 100644 --- a/spring-test/src/test/java/org/springframework/test/json/JsonContentTests.java +++ b/spring-test/src/test/java/org/springframework/test/json/JsonContentTests.java @@ -18,13 +18,17 @@ import org.junit.jupiter.api.Test; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.Mockito.mock; /** * Tests for {@link JsonContent}. * * @author Phillip Webb + * @author Stephane Nicoll */ class JsonContentTests { @@ -34,27 +38,33 @@ class JsonContentTests { void createWhenJsonIsNullShouldThrowException() { assertThatIllegalArgumentException() .isThrownBy( - () -> new JsonContent(null, null)) + () -> new JsonContent(null)) .withMessageContaining("JSON must not be null"); } @Test - @SuppressWarnings("deprecation") void assertThatShouldReturnJsonContentAssert() { - JsonContent content = new JsonContent(JSON, getClass()); + JsonContent content = new JsonContent(JSON); assertThat(content.assertThat()).isInstanceOf(JsonContentAssert.class); } @Test void getJsonShouldReturnJson() { - JsonContent content = new JsonContent(JSON, getClass()); + JsonContent content = new JsonContent(JSON); assertThat(content.getJson()).isEqualTo(JSON); } @Test void toStringShouldReturnString() { - JsonContent content = new JsonContent(JSON, getClass()); + JsonContent content = new JsonContent(JSON); assertThat(content.toString()).isEqualTo("JsonContent " + JSON); } + @Test + void getJsonMessageConverterShouldReturnConverter() { + MappingJackson2HttpMessageConverter converter = mock(MappingJackson2HttpMessageConverter.class); + JsonContent content = new JsonContent(JSON, converter); + assertThat(content.getJsonMessageConverter()).isSameAs(converter); + } + }