Allow JSON content assertions to be nested
Previously, AbstractJsonContentAssert worked on a raw String, which made standard AssertJ nested calls, such as satisfies, to return an assert on the raw string, rather than one with JSON support. This commit rework AbstractJsonContentAssert so that it no longer extend from AbstractStringAssert. This makes the list of methods more focused on JSON assertions, and allow standard operations to provide the right assert object. Closes gh-32894
This commit is contained in:
parent
489d18a169
commit
e2c5887cea
|
|
@ -24,8 +24,9 @@ import java.util.function.Consumer;
|
|||
|
||||
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 @@ import org.springframework.util.Assert;
|
|||
* @param <SELF> the type of assertions
|
||||
*/
|
||||
public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContentAssert<SELF>>
|
||||
extends AbstractStringAssert<SELF> {
|
||||
extends AbstractObjectAssert<SELF, JsonContent> {
|
||||
|
||||
private static final Failures failures = Failures.instance();
|
||||
|
||||
|
|
@ -79,16 +80,12 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
|
|||
|
||||
/**
|
||||
* Create an assert for the given JSON document.
|
||||
* <p>Path 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<Object> 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 abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
|
|||
|
||||
// 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 abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
|
|||
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 abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
|
|||
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 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package org.springframework.test.json;
|
|||
|
||||
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<JsonContentAssert> {
|
|||
private final String json;
|
||||
|
||||
@Nullable
|
||||
private final Class<?> resourceLoadClass;
|
||||
private final GenericHttpMessageConverter<Object> jsonMessageConverter;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code JsonContent} instance with the message converter to
|
||||
* use to deserialize content.
|
||||
* @param json the actual JSON content
|
||||
* @param jsonMessageConverter the message converter to use
|
||||
*/
|
||||
public JsonContent(String json, @Nullable GenericHttpMessageConverter<Object> jsonMessageConverter) {
|
||||
Assert.notNull(json, "JSON must not be null");
|
||||
this.json = json;
|
||||
this.jsonMessageConverter = jsonMessageConverter;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code JsonContent} instance.
|
||||
* @param json the actual JSON content
|
||||
* @param resourceLoadClass the source class used to load resources
|
||||
*/
|
||||
JsonContent(String json, @Nullable Class<?> resourceLoadClass) {
|
||||
Assert.notNull(json, "JSON must not be null");
|
||||
this.json = json;
|
||||
this.resourceLoadClass = resourceLoadClass;
|
||||
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<Object> getJsonMessageConverter() {
|
||||
return this.jsonMessageConverter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JsonContent " + this.json;
|
||||
|
|
|
|||
|
|
@ -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 AbstractJsonContentAssert<JsonContentAsse
|
|||
|
||||
/**
|
||||
* Create an assert for the given JSON document.
|
||||
* <p>Path 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<Object> jsonMessageConverter) {
|
||||
super(json, jsonMessageConverter, JsonContentAssert.class);
|
||||
public JsonContentAssert(@Nullable JsonContent json) {
|
||||
super(json, JsonContentAssert.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import org.springframework.http.converter.GenericHttpMessageConverter;
|
|||
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 abstract class AbstractMockHttpServletResponseAssert<SELF extends Abstrac
|
|||
* </code></pre>
|
||||
*/
|
||||
public AbstractJsonContentAssert<?> bodyJson() {
|
||||
return new JsonContentAssert(readBody(), this.jsonMessageConverter);
|
||||
return new JsonContentAssert(new JsonContent(readBody(), this.jsonMessageConverter));
|
||||
}
|
||||
|
||||
private String readBody() {
|
||||
|
|
|
|||
|
|
@ -92,6 +92,14 @@ class AbstractJsonContentAssertTests {
|
|||
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 @@ class AbstractJsonContentAssertTests {
|
|||
private static class TestJsonContentAssert extends AbstractJsonContentAssert<TestJsonContentAssert> {
|
||||
|
||||
public TestJsonContentAssert(@Nullable String json, @Nullable GenericHttpMessageConverter<Object> jsonMessageConverter) {
|
||||
super(json, jsonMessageConverter, TestJsonContentAssert.class);
|
||||
super((json != null ? new JsonContent(json, jsonMessageConverter) : null), TestJsonContentAssert.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,13 +18,17 @@ package org.springframework.test.json;
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue