commit
168276aaab
|
|
@ -0,0 +1,528 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.json;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Path;
|
||||
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.AssertProvider;
|
||||
import org.assertj.core.error.BasicErrorMessageFactory;
|
||||
import org.assertj.core.internal.Failures;
|
||||
import org.skyscreamer.jsonassert.JSONCompare;
|
||||
import org.skyscreamer.jsonassert.JSONCompareMode;
|
||||
import org.skyscreamer.jsonassert.JSONCompareResult;
|
||||
import org.skyscreamer.jsonassert.comparator.JSONComparator;
|
||||
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.converter.GenericHttpMessageConverter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.function.ThrowingBiFunction;
|
||||
|
||||
/**
|
||||
* Base AssertJ {@link org.assertj.core.api.Assert assertions} that can be
|
||||
* applied to a JSON document.
|
||||
*
|
||||
* <p>Support evaluating {@linkplain JsonPath JSON path} expressions and
|
||||
* extracting a part of the document for further {@linkplain JsonPathValueAssert
|
||||
* assertions} on the value.
|
||||
*
|
||||
* <p>Also support comparing the JSON document against a target, using
|
||||
* {@linkplain JSONCompare JSON Assert}. Resources that are loaded from
|
||||
* the classpath can be relative if a {@linkplain #withResourceLoadClass(Class)
|
||||
* class} is provided. By default, {@code UTF-8} is used to load resources
|
||||
* but this can be overridden using {@link #withCharset(Charset)}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Diego Berrueta
|
||||
* @author Camille Vienot
|
||||
* @since 6.2
|
||||
* @param <SELF> the type of assertions
|
||||
*/
|
||||
public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContentAssert<SELF>>
|
||||
extends AbstractStringAssert<SELF> {
|
||||
|
||||
private static final Failures failures = Failures.instance();
|
||||
|
||||
|
||||
@Nullable
|
||||
private final GenericHttpMessageConverter<Object> jsonMessageConverter;
|
||||
|
||||
@Nullable
|
||||
private Class<?> resourceLoadClass;
|
||||
|
||||
@Nullable
|
||||
private Charset charset;
|
||||
|
||||
private JsonLoader jsonLoader;
|
||||
|
||||
/**
|
||||
* 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 selfType the implementation type of this assert
|
||||
*/
|
||||
protected AbstractJsonContentAssert(@Nullable String json,
|
||||
@Nullable GenericHttpMessageConverter<Object> jsonMessageConverter, Class<?> selfType) {
|
||||
super(json, selfType);
|
||||
this.jsonMessageConverter = jsonMessageConverter;
|
||||
this.jsonLoader = new JsonLoader(null, null);
|
||||
as("JSON content");
|
||||
}
|
||||
|
||||
// JsonPath support
|
||||
|
||||
/**
|
||||
* Verify that the given JSON {@code path} is present, and extract the JSON
|
||||
* value for further {@linkplain JsonPathValueAssert assertions}.
|
||||
* @param path the {@link JsonPath} expression
|
||||
* @see #hasPathSatisfying(String, Consumer)
|
||||
*/
|
||||
public JsonPathValueAssert extractingPath(String path) {
|
||||
Object value = new JsonPathValue(path).getValue();
|
||||
return new JsonPathValueAssert(value, path, this.jsonMessageConverter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the given JSON {@code path} is present with a JSON value
|
||||
* satisfying the given {@code valueRequirements}.
|
||||
* @param path the {@link JsonPath} expression
|
||||
* @param valueRequirements a {@link Consumer} of the assertion object
|
||||
*/
|
||||
public SELF hasPathSatisfying(String path, Consumer<AssertProvider<JsonPathValueAssert>> valueRequirements) {
|
||||
Object value = new JsonPathValue(path).assertHasPath();
|
||||
JsonPathValueAssert valueAssert = new JsonPathValueAssert(value, path, this.jsonMessageConverter);
|
||||
valueRequirements.accept(() -> valueAssert);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the given JSON {@code path} matches. For paths with an
|
||||
* operator, this validates that the path expression is valid, but does not
|
||||
* validate that it yield any results.
|
||||
* @param path the {@link JsonPath} expression
|
||||
*/
|
||||
public SELF hasPath(String path) {
|
||||
new JsonPathValue(path).assertHasPath();
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the given JSON {@code path} does not match.
|
||||
* @param path the {@link JsonPath} expression
|
||||
*/
|
||||
public SELF doesNotHavePath(String path) {
|
||||
new JsonPathValue(path).assertDoesNotHavePath();
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
// JsonAssert support
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@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
|
||||
* @param compareMode the compare mode used when checking
|
||||
*/
|
||||
public SELF isEqualTo(@Nullable CharSequence expected, JSONCompareMode compareMode) {
|
||||
String expectedJson = this.jsonLoader.getJson(expected);
|
||||
return assertNotFailed(compare(expectedJson, compareMode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is equal to the given JSON {@link Resource}.
|
||||
* <p>The resource abstraction allows to provide several input types:
|
||||
* <ul>
|
||||
* <li>a {@code byte} array, using {@link ByteArrayResource}</li>
|
||||
* <li>a {@code classpath} resource, using {@link ClassPathResource}</li>
|
||||
* <li>a {@link File} or {@link Path}, using {@link FileSystemResource}</li>
|
||||
* <li>an {@link InputStream}, using {@link InputStreamResource}</li>
|
||||
* </ul>
|
||||
* @param expected a resource containing the expected JSON
|
||||
* @param compareMode the compare mode used when checking
|
||||
*/
|
||||
public SELF isEqualTo(Resource expected, JSONCompareMode compareMode) {
|
||||
String expectedJson = this.jsonLoader.getJson(expected);
|
||||
return assertNotFailed(compare(expectedJson, compareMode));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@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
|
||||
* @param comparator the comparator used when checking
|
||||
*/
|
||||
public SELF isEqualTo(@Nullable CharSequence expected, JSONComparator comparator) {
|
||||
String expectedJson = this.jsonLoader.getJson(expected);
|
||||
return assertNotFailed(compare(expectedJson, comparator));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is equal to the given JSON {@link Resource}.
|
||||
* <p>The resource abstraction allows to provide several input types:
|
||||
* <ul>
|
||||
* <li>a {@code byte} array, using {@link ByteArrayResource}</li>
|
||||
* <li>a {@code classpath} resource, using {@link ClassPathResource}</li>
|
||||
* <li>a {@link File} or {@link Path}, using {@link FileSystemResource}</li>
|
||||
* <li>an {@link InputStream}, using {@link InputStreamResource}</li>
|
||||
* </ul>
|
||||
* @param expected a resource containing the expected JSON
|
||||
* @param comparator the comparator used when checking
|
||||
*/
|
||||
public SELF isEqualTo(Resource expected, JSONComparator comparator) {
|
||||
String expectedJson = this.jsonLoader.getJson(expected);
|
||||
return assertNotFailed(compare(expectedJson, comparator));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is {@link JSONCompareMode#LENIENT leniently}
|
||||
* 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
|
||||
*/
|
||||
public SELF isLenientlyEqualTo(@Nullable CharSequence expected) {
|
||||
return isEqualTo(expected, JSONCompareMode.LENIENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is {@link JSONCompareMode#LENIENT leniently}
|
||||
* equal to the given JSON {@link Resource}.
|
||||
* <p>The resource abstraction allows to provide several input types:
|
||||
* <ul>
|
||||
* <li>a {@code byte} array, using {@link ByteArrayResource}</li>
|
||||
* <li>a {@code classpath} resource, using {@link ClassPathResource}</li>
|
||||
* <li>a {@link File} or {@link Path}, using {@link FileSystemResource}</li>
|
||||
* <li>an {@link InputStream}, using {@link InputStreamResource}</li>
|
||||
* </ul>
|
||||
* @param expected a resource containing the expected JSON
|
||||
*/
|
||||
public SELF isLenientlyEqualTo(Resource expected) {
|
||||
return isEqualTo(expected, JSONCompareMode.LENIENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is {@link 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
|
||||
*/
|
||||
public SELF isStrictlyEqualTo(@Nullable CharSequence expected) {
|
||||
return isEqualTo(expected, JSONCompareMode.STRICT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is {@link JSONCompareMode#STRICT strictly}
|
||||
* equal to the given JSON {@link Resource}.
|
||||
* <p>The resource abstraction allows to provide several input types:
|
||||
* <ul>
|
||||
* <li>a {@code byte} array, using {@link ByteArrayResource}</li>
|
||||
* <li>a {@code classpath} resource, using {@link ClassPathResource}</li>
|
||||
* <li>a {@link File} or {@link Path}, using {@link FileSystemResource}</li>
|
||||
* <li>an {@link InputStream}, using {@link InputStreamResource}</li>
|
||||
* </ul>
|
||||
* @param expected a resource containing the expected JSON
|
||||
*/
|
||||
public SELF isStrictlyEqualTo(Resource expected) {
|
||||
return isEqualTo(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
|
||||
* {@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
|
||||
* @param compareMode the compare mode used when checking
|
||||
*/
|
||||
public SELF isNotEqualTo(@Nullable CharSequence expected, JSONCompareMode compareMode) {
|
||||
String expectedJson = this.jsonLoader.getJson(expected);
|
||||
return assertNotPassed(compare(expectedJson, compareMode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is not equal to the given JSON {@link Resource}.
|
||||
* <p>The resource abstraction allows to provide several input types:
|
||||
* <ul>
|
||||
* <li>a {@code byte} array, using {@link ByteArrayResource}</li>
|
||||
* <li>a {@code classpath} resource, using {@link ClassPathResource}</li>
|
||||
* <li>a {@link File} or {@link Path}, using {@link FileSystemResource}</li>
|
||||
* <li>an {@link InputStream}, using {@link InputStreamResource}</li>
|
||||
* </ul>
|
||||
* @param expected a resource containing the expected JSON
|
||||
* @param compareMode the compare mode used when checking
|
||||
*/
|
||||
public SELF isNotEqualTo(Resource expected, JSONCompareMode compareMode) {
|
||||
String expectedJson = this.jsonLoader.getJson(expected);
|
||||
return assertNotPassed(compare(expectedJson, compareMode));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@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
|
||||
* @param comparator the comparator used when checking
|
||||
*/
|
||||
public SELF isNotEqualTo(@Nullable CharSequence expected, JSONComparator comparator) {
|
||||
String expectedJson = this.jsonLoader.getJson(expected);
|
||||
return assertNotPassed(compare(expectedJson, comparator));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is not equal to the given JSON {@link Resource}.
|
||||
* <p>The resource abstraction allows to provide several input types:
|
||||
* <ul>
|
||||
* <li>a {@code byte} array, using {@link ByteArrayResource}</li>
|
||||
* <li>a {@code classpath} resource, using {@link ClassPathResource}</li>
|
||||
* <li>a {@link File} or {@link Path}, using {@link FileSystemResource}</li>
|
||||
* <li>an {@link InputStream}, using {@link InputStreamResource}</li>
|
||||
* </ul>
|
||||
* @param expected a resource containing the expected JSON
|
||||
* @param comparator the comparator used when checking
|
||||
*/
|
||||
public SELF isNotEqualTo(Resource expected, JSONComparator comparator) {
|
||||
String expectedJson = this.jsonLoader.getJson(expected);
|
||||
return assertNotPassed(compare(expectedJson, comparator));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is not {@link JSONCompareMode#LENIENT
|
||||
* leniently} 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
|
||||
*/
|
||||
public SELF isNotLenientlyEqualTo(@Nullable CharSequence expected) {
|
||||
return isNotEqualTo(expected, JSONCompareMode.LENIENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is not {@link JSONCompareMode#LENIENT
|
||||
* leniently} equal to the given JSON {@link Resource}.
|
||||
* <p>The resource abstraction allows to provide several input types:
|
||||
* <ul>
|
||||
* <li>a {@code byte} array, using {@link ByteArrayResource}</li>
|
||||
* <li>a {@code classpath} resource, using {@link ClassPathResource}</li>
|
||||
* <li>a {@link File} or {@link Path}, using {@link FileSystemResource}</li>
|
||||
* <li>an {@link InputStream}, using {@link InputStreamResource}</li>
|
||||
* </ul>
|
||||
* @param expected a resource containing the expected JSON
|
||||
*/
|
||||
public SELF isNotLenientlyEqualTo(Resource expected) {
|
||||
return isNotEqualTo(expected, JSONCompareMode.LENIENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is not {@link 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
|
||||
*/
|
||||
public SELF isNotStrictlyEqualTo(@Nullable CharSequence expected) {
|
||||
return isNotEqualTo(expected, JSONCompareMode.STRICT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is not {@link JSONCompareMode#STRICT
|
||||
* strictly} equal to the given JSON {@link Resource}.
|
||||
* <p>The resource abstraction allows to provide several input types:
|
||||
* <ul>
|
||||
* <li>a {@code byte} array, using {@link ByteArrayResource}</li>
|
||||
* <li>a {@code classpath} resource, using {@link ClassPathResource}</li>
|
||||
* <li>a {@link File} or {@link Path}, using {@link FileSystemResource}</li>
|
||||
* <li>an {@link InputStream}, using {@link InputStreamResource}</li>
|
||||
* </ul>
|
||||
* @param expected a resource containing the expected JSON
|
||||
*/
|
||||
public SELF isNotStrictlyEqualTo(Resource expected) {
|
||||
return isNotEqualTo(expected, JSONCompareMode.STRICT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the class used to load resources. Resources can be loaded from
|
||||
* an absolute location or relative to tpe specified class. For instance,
|
||||
* specifying {@code com.example.MyClass} as the resource class allows you
|
||||
* to use "my-file.json" to load {@code /com/example/my-file.json}.
|
||||
* @param resourceLoadClass the class used to load resources or {@code null}
|
||||
* to only use absolute paths.
|
||||
*/
|
||||
public SELF withResourceLoadClass(@Nullable Class<?> resourceLoadClass) {
|
||||
this.resourceLoadClass = resourceLoadClass;
|
||||
this.jsonLoader = new JsonLoader(resourceLoadClass, this.charset);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the {@link Charset} to use to load resources. By default,
|
||||
* resources are loaded using {@code UTF-8}.
|
||||
* @param charset the charset to use, or {@code null} to use the default
|
||||
*/
|
||||
public SELF withCharset(@Nullable Charset charset) {
|
||||
this.charset = charset;
|
||||
this.jsonLoader = new JsonLoader(this.resourceLoadClass, charset);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
|
||||
private JSONCompareResult compare(@Nullable CharSequence expectedJson, JSONCompareMode compareMode) {
|
||||
return compare(this.actual, expectedJson, (actualJsonString, expectedJsonString) ->
|
||||
JSONCompare.compareJSON(expectedJsonString, actualJsonString, compareMode));
|
||||
}
|
||||
|
||||
private JSONCompareResult compare(@Nullable CharSequence expectedJson, JSONComparator comparator) {
|
||||
return compare(this.actual, expectedJson, (actualJsonString, expectedJsonString) ->
|
||||
JSONCompare.compareJSON(expectedJsonString, actualJsonString, comparator));
|
||||
}
|
||||
|
||||
private JSONCompareResult compare(@Nullable CharSequence actualJson, @Nullable CharSequence expectedJson,
|
||||
ThrowingBiFunction<String, String, JSONCompareResult> comparator) {
|
||||
|
||||
if (actualJson == null) {
|
||||
return compareForNull(expectedJson);
|
||||
}
|
||||
if (expectedJson == null) {
|
||||
return compareForNull(actualJson.toString());
|
||||
}
|
||||
try {
|
||||
return comparator.applyWithException(actualJson.toString(), expectedJson.toString());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (ex instanceof RuntimeException runtimeException) {
|
||||
throw runtimeException;
|
||||
}
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private JSONCompareResult compareForNull(@Nullable CharSequence expectedJson) {
|
||||
JSONCompareResult result = new JSONCompareResult();
|
||||
if (expectedJson != null) {
|
||||
result.fail("Expected null JSON");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private SELF assertNotFailed(JSONCompareResult result) {
|
||||
if (result.failed()) {
|
||||
failWithMessage("JSON comparison failure: %s", result.getMessage());
|
||||
}
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
private SELF assertNotPassed(JSONCompareResult result) {
|
||||
if (result.passed()) {
|
||||
failWithMessage("JSON comparison failure: %s", result.getMessage());
|
||||
}
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
private AssertionError failure(BasicErrorMessageFactory errorMessageFactory) {
|
||||
throw failures.failure(this.info, errorMessageFactory);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A {@link JsonPath} value.
|
||||
*/
|
||||
private class JsonPathValue {
|
||||
|
||||
private final String path;
|
||||
|
||||
private final JsonPath jsonPath;
|
||||
|
||||
private final String json;
|
||||
|
||||
JsonPathValue(String path) {
|
||||
Assert.hasText(path, "'path' must not be null or empty");
|
||||
isNotNull();
|
||||
this.path = path;
|
||||
this.jsonPath = JsonPath.compile(this.path);
|
||||
this.json = AbstractJsonContentAssert.this.actual;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Object assertHasPath() {
|
||||
return getValue();
|
||||
}
|
||||
|
||||
void assertDoesNotHavePath() {
|
||||
try {
|
||||
read();
|
||||
throw failure(new JsonPathNotExpected(this.json, this.path));
|
||||
}
|
||||
catch (PathNotFoundException ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Object getValue() {
|
||||
try {
|
||||
return read();
|
||||
}
|
||||
catch (PathNotFoundException ex) {
|
||||
throw failure(new JsonPathNotFound(this.json, this.path));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object read() {
|
||||
return this.jsonPath.read(this.json);
|
||||
}
|
||||
|
||||
|
||||
static final class JsonPathNotFound extends BasicErrorMessageFactory {
|
||||
|
||||
private JsonPathNotFound(String actual, String path) {
|
||||
super("%nExpecting:%n %s%nTo match JSON path:%n %s%n", actual, path);
|
||||
}
|
||||
}
|
||||
|
||||
static final class JsonPathNotExpected extends BasicErrorMessageFactory {
|
||||
|
||||
private JsonPathNotExpected(String actual, String path) {
|
||||
super("%nExpecting:%n %s%nNot to match JSON path:%n %s%n", actual, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -55,7 +55,7 @@ public final class JsonContent implements AssertProvider<JsonContentAssert> {
|
|||
*/
|
||||
@Override
|
||||
public JsonContentAssert assertThat() {
|
||||
return new JsonContentAssert(this.json, this.resourceLoadClass, null);
|
||||
return new JsonContentAssert(this.json, null).withResourceLoadClass(this.resourceLoadClass);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -16,351 +16,26 @@
|
|||
|
||||
package org.springframework.test.json;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.assertj.core.api.AbstractAssert;
|
||||
import org.skyscreamer.jsonassert.JSONCompare;
|
||||
import org.skyscreamer.jsonassert.JSONCompareMode;
|
||||
import org.skyscreamer.jsonassert.JSONCompareResult;
|
||||
import org.skyscreamer.jsonassert.comparator.JSONComparator;
|
||||
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.converter.GenericHttpMessageConverter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.function.ThrowingBiFunction;
|
||||
|
||||
/**
|
||||
* AssertJ {@link org.assertj.core.api.Assert assertions} that can be applied
|
||||
* to a {@link CharSequence} representation of a JSON document, mostly to
|
||||
* compare the JSON document against a target, using {@linkplain JSONCompare
|
||||
* JSON Assert}.
|
||||
* Default {@link AbstractJsonContentAssert} implementation.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Diego Berrueta
|
||||
* @author Camille Vienot
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.2
|
||||
*/
|
||||
public class JsonContentAssert extends AbstractAssert<JsonContentAssert, CharSequence> {
|
||||
|
||||
private final JsonLoader loader;
|
||||
public class JsonContentAssert extends AbstractJsonContentAssert<JsonContentAssert> {
|
||||
|
||||
/**
|
||||
* Create a new {@link JsonContentAssert} instance that will load resources
|
||||
* relative to the given {@code resourceLoadClass}, using the given
|
||||
* {@code charset}.
|
||||
* @param json the actual JSON content
|
||||
* @param resourceLoadClass the class used to load resources
|
||||
* @param charset the charset of the JSON resources
|
||||
* 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 CharSequence json, @Nullable Class<?> resourceLoadClass,
|
||||
@Nullable Charset charset) {
|
||||
|
||||
super(json, JsonContentAssert.class);
|
||||
this.loader = new JsonLoader(resourceLoadClass, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link JsonContentAssert} instance that will load resources
|
||||
* relative to the given {@code resourceLoadClass}, using {@code UTF-8}.
|
||||
* @param json the actual JSON content
|
||||
* @param resourceLoadClass the class used to load resources
|
||||
*/
|
||||
public JsonContentAssert(@Nullable CharSequence json, @Nullable Class<?> resourceLoadClass) {
|
||||
this(json, resourceLoadClass, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@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
|
||||
* @param compareMode the compare mode used when checking
|
||||
*/
|
||||
public JsonContentAssert isEqualTo(@Nullable CharSequence expected, JSONCompareMode compareMode) {
|
||||
String expectedJson = this.loader.getJson(expected);
|
||||
return assertNotFailed(compare(expectedJson, compareMode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is equal to the given JSON {@link Resource}.
|
||||
* <p>The resource abstraction allows to provide several input types:
|
||||
* <ul>
|
||||
* <li>a {@code byte} array, using {@link ByteArrayResource}</li>
|
||||
* <li>a {@code classpath} resource, using {@link ClassPathResource}</li>
|
||||
* <li>a {@link File} or {@link Path}, using {@link FileSystemResource}</li>
|
||||
* <li>an {@link InputStream}, using {@link InputStreamResource}</li>
|
||||
* </ul>
|
||||
* @param expected a resource containing the expected JSON
|
||||
* @param compareMode the compare mode used when checking
|
||||
*/
|
||||
public JsonContentAssert isEqualTo(Resource expected, JSONCompareMode compareMode) {
|
||||
String expectedJson = this.loader.getJson(expected);
|
||||
return assertNotFailed(compare(expectedJson, compareMode));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@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
|
||||
* @param comparator the comparator used when checking
|
||||
*/
|
||||
public JsonContentAssert isEqualTo(@Nullable CharSequence expected, JSONComparator comparator) {
|
||||
String expectedJson = this.loader.getJson(expected);
|
||||
return assertNotFailed(compare(expectedJson, comparator));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is equal to the given JSON {@link Resource}.
|
||||
* <p>The resource abstraction allows to provide several input types:
|
||||
* <ul>
|
||||
* <li>a {@code byte} array, using {@link ByteArrayResource}</li>
|
||||
* <li>a {@code classpath} resource, using {@link ClassPathResource}</li>
|
||||
* <li>a {@link File} or {@link Path}, using {@link FileSystemResource}</li>
|
||||
* <li>an {@link InputStream}, using {@link InputStreamResource}</li>
|
||||
* </ul>
|
||||
* @param expected a resource containing the expected JSON
|
||||
* @param comparator the comparator used when checking
|
||||
*/
|
||||
public JsonContentAssert isEqualTo(Resource expected, JSONComparator comparator) {
|
||||
String expectedJson = this.loader.getJson(expected);
|
||||
return assertNotFailed(compare(expectedJson, comparator));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is {@link JSONCompareMode#LENIENT leniently}
|
||||
* 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
|
||||
*/
|
||||
public JsonContentAssert isLenientlyEqualTo(@Nullable CharSequence expected) {
|
||||
return isEqualTo(expected, JSONCompareMode.LENIENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is {@link JSONCompareMode#LENIENT leniently}
|
||||
* equal to the given JSON {@link Resource}.
|
||||
* <p>The resource abstraction allows to provide several input types:
|
||||
* <ul>
|
||||
* <li>a {@code byte} array, using {@link ByteArrayResource}</li>
|
||||
* <li>a {@code classpath} resource, using {@link ClassPathResource}</li>
|
||||
* <li>a {@link File} or {@link Path}, using {@link FileSystemResource}</li>
|
||||
* <li>an {@link InputStream}, using {@link InputStreamResource}</li>
|
||||
* </ul>
|
||||
* @param expected a resource containing the expected JSON
|
||||
*/
|
||||
public JsonContentAssert isLenientlyEqualTo(Resource expected) {
|
||||
return isEqualTo(expected, JSONCompareMode.LENIENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is {@link 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
|
||||
*/
|
||||
public JsonContentAssert isStrictlyEqualTo(@Nullable CharSequence expected) {
|
||||
return isEqualTo(expected, JSONCompareMode.STRICT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is {@link JSONCompareMode#STRICT strictly}
|
||||
* equal to the given JSON {@link Resource}.
|
||||
* <p>The resource abstraction allows to provide several input types:
|
||||
* <ul>
|
||||
* <li>a {@code byte} array, using {@link ByteArrayResource}</li>
|
||||
* <li>a {@code classpath} resource, using {@link ClassPathResource}</li>
|
||||
* <li>a {@link File} or {@link Path}, using {@link FileSystemResource}</li>
|
||||
* <li>an {@link InputStream}, using {@link InputStreamResource}</li>
|
||||
* </ul>
|
||||
* @param expected a resource containing the expected JSON
|
||||
*/
|
||||
public JsonContentAssert isStrictlyEqualTo(Resource expected) {
|
||||
return isEqualTo(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
|
||||
* {@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
|
||||
* @param compareMode the compare mode used when checking
|
||||
*/
|
||||
public JsonContentAssert isNotEqualTo(@Nullable CharSequence expected, JSONCompareMode compareMode) {
|
||||
String expectedJson = this.loader.getJson(expected);
|
||||
return assertNotPassed(compare(expectedJson, compareMode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is not equal to the given JSON {@link Resource}.
|
||||
* <p>The resource abstraction allows to provide several input types:
|
||||
* <ul>
|
||||
* <li>a {@code byte} array, using {@link ByteArrayResource}</li>
|
||||
* <li>a {@code classpath} resource, using {@link ClassPathResource}</li>
|
||||
* <li>a {@link File} or {@link Path}, using {@link FileSystemResource}</li>
|
||||
* <li>an {@link InputStream}, using {@link InputStreamResource}</li>
|
||||
* </ul>
|
||||
* @param expected a resource containing the expected JSON
|
||||
* @param compareMode the compare mode used when checking
|
||||
*/
|
||||
public JsonContentAssert isNotEqualTo(Resource expected, JSONCompareMode compareMode) {
|
||||
String expectedJson = this.loader.getJson(expected);
|
||||
return assertNotPassed(compare(expectedJson, compareMode));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@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
|
||||
* @param comparator the comparator used when checking
|
||||
*/
|
||||
public JsonContentAssert isNotEqualTo(@Nullable CharSequence expected, JSONComparator comparator) {
|
||||
String expectedJson = this.loader.getJson(expected);
|
||||
return assertNotPassed(compare(expectedJson, comparator));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is not equal to the given JSON {@link Resource}.
|
||||
* <p>The resource abstraction allows to provide several input types:
|
||||
* <ul>
|
||||
* <li>a {@code byte} array, using {@link ByteArrayResource}</li>
|
||||
* <li>a {@code classpath} resource, using {@link ClassPathResource}</li>
|
||||
* <li>a {@link File} or {@link Path}, using {@link FileSystemResource}</li>
|
||||
* <li>an {@link InputStream}, using {@link InputStreamResource}</li>
|
||||
* </ul>
|
||||
* @param expected a resource containing the expected JSON
|
||||
* @param comparator the comparator used when checking
|
||||
*/
|
||||
public JsonContentAssert isNotEqualTo(Resource expected, JSONComparator comparator) {
|
||||
String expectedJson = this.loader.getJson(expected);
|
||||
return assertNotPassed(compare(expectedJson, comparator));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is not {@link JSONCompareMode#LENIENT
|
||||
* leniently} 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
|
||||
*/
|
||||
public JsonContentAssert isNotLenientlyEqualTo(@Nullable CharSequence expected) {
|
||||
return isNotEqualTo(expected, JSONCompareMode.LENIENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is not {@link JSONCompareMode#LENIENT
|
||||
* leniently} equal to the given JSON {@link Resource}.
|
||||
* <p>The resource abstraction allows to provide several input types:
|
||||
* <ul>
|
||||
* <li>a {@code byte} array, using {@link ByteArrayResource}</li>
|
||||
* <li>a {@code classpath} resource, using {@link ClassPathResource}</li>
|
||||
* <li>a {@link File} or {@link Path}, using {@link FileSystemResource}</li>
|
||||
* <li>an {@link InputStream}, using {@link InputStreamResource}</li>
|
||||
* </ul>
|
||||
* @param expected a resource containing the expected JSON
|
||||
*/
|
||||
public JsonContentAssert isNotLenientlyEqualTo(Resource expected) {
|
||||
return isNotEqualTo(expected, JSONCompareMode.LENIENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is not {@link 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
|
||||
*/
|
||||
public JsonContentAssert isNotStrictlyEqualTo(@Nullable CharSequence expected) {
|
||||
return isNotEqualTo(expected, JSONCompareMode.STRICT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual value is not {@link JSONCompareMode#STRICT
|
||||
* strictly} equal to the given JSON {@link Resource}.
|
||||
* <p>The resource abstraction allows to provide several input types:
|
||||
* <ul>
|
||||
* <li>a {@code byte} array, using {@link ByteArrayResource}</li>
|
||||
* <li>a {@code classpath} resource, using {@link ClassPathResource}</li>
|
||||
* <li>a {@link File} or {@link Path}, using {@link FileSystemResource}</li>
|
||||
* <li>an {@link InputStream}, using {@link InputStreamResource}</li>
|
||||
* </ul>
|
||||
* @param expected a resource containing the expected JSON
|
||||
*/
|
||||
public JsonContentAssert isNotStrictlyEqualTo(Resource expected) {
|
||||
return isNotEqualTo(expected, JSONCompareMode.STRICT);
|
||||
}
|
||||
|
||||
|
||||
private JSONCompareResult compare(@Nullable CharSequence expectedJson, JSONCompareMode compareMode) {
|
||||
return compare(this.actual, expectedJson, (actualJsonString, expectedJsonString) ->
|
||||
JSONCompare.compareJSON(expectedJsonString, actualJsonString, compareMode));
|
||||
}
|
||||
|
||||
private JSONCompareResult compare(@Nullable CharSequence expectedJson, JSONComparator comparator) {
|
||||
return compare(this.actual, expectedJson, (actualJsonString, expectedJsonString) ->
|
||||
JSONCompare.compareJSON(expectedJsonString, actualJsonString, comparator));
|
||||
}
|
||||
|
||||
private JSONCompareResult compare(@Nullable CharSequence actualJson, @Nullable CharSequence expectedJson,
|
||||
ThrowingBiFunction<String, String, JSONCompareResult> comparator) {
|
||||
|
||||
if (actualJson == null) {
|
||||
return compareForNull(expectedJson);
|
||||
}
|
||||
if (expectedJson == null) {
|
||||
return compareForNull(actualJson.toString());
|
||||
}
|
||||
try {
|
||||
return comparator.applyWithException(actualJson.toString(), expectedJson.toString());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (ex instanceof RuntimeException runtimeException) {
|
||||
throw runtimeException;
|
||||
}
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private JSONCompareResult compareForNull(@Nullable CharSequence expectedJson) {
|
||||
JSONCompareResult result = new JSONCompareResult();
|
||||
if (expectedJson != null) {
|
||||
result.fail("Expected null JSON");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private JsonContentAssert assertNotFailed(JSONCompareResult result) {
|
||||
if (result.failed()) {
|
||||
failWithMessage("JSON comparison failure: %s", result.getMessage());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private JsonContentAssert assertNotPassed(JSONCompareResult result) {
|
||||
if (result.passed()) {
|
||||
failWithMessage("JSON comparison failure: %s", result.getMessage());
|
||||
}
|
||||
return this;
|
||||
public JsonContentAssert(@Nullable String json, @Nullable GenericHttpMessageConverter<Object> jsonMessageConverter) {
|
||||
super(json, jsonMessageConverter, JsonContentAssert.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,170 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.json;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
import com.jayway.jsonpath.PathNotFoundException;
|
||||
import org.assertj.core.api.AbstractAssert;
|
||||
import org.assertj.core.api.AssertProvider;
|
||||
import org.assertj.core.error.BasicErrorMessageFactory;
|
||||
import org.assertj.core.internal.Failures;
|
||||
|
||||
import org.springframework.http.converter.GenericHttpMessageConverter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* AssertJ {@link org.assertj.core.api.Assert assertions} that can be applied
|
||||
* to a {@link CharSequence} representation of a JSON document using
|
||||
* {@linkplain JsonPath JSON path}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.2
|
||||
*/
|
||||
public class JsonPathAssert extends AbstractAssert<JsonPathAssert, CharSequence> {
|
||||
|
||||
private static final Failures failures = Failures.instance();
|
||||
|
||||
|
||||
@Nullable
|
||||
private final GenericHttpMessageConverter<Object> jsonMessageConverter;
|
||||
|
||||
|
||||
public JsonPathAssert(CharSequence json,
|
||||
@Nullable GenericHttpMessageConverter<Object> jsonMessageConverter) {
|
||||
|
||||
super(json, JsonPathAssert.class);
|
||||
this.jsonMessageConverter = jsonMessageConverter;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verify that the given JSON {@code path} is present, and extract the JSON
|
||||
* value for further {@linkplain JsonPathValueAssert assertions}.
|
||||
* @param path the {@link JsonPath} expression
|
||||
* @see #hasPathSatisfying(String, Consumer)
|
||||
*/
|
||||
public JsonPathValueAssert extractingPath(String path) {
|
||||
Object value = new JsonPathValue(path).getValue();
|
||||
return new JsonPathValueAssert(value, path, this.jsonMessageConverter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the given JSON {@code path} is present with a JSON value
|
||||
* satisfying the given {@code valueRequirements}.
|
||||
* @param path the {@link JsonPath} expression
|
||||
* @param valueRequirements a {@link Consumer} of the assertion object
|
||||
*/
|
||||
public JsonPathAssert hasPathSatisfying(String path, Consumer<AssertProvider<JsonPathValueAssert>> valueRequirements) {
|
||||
Object value = new JsonPathValue(path).assertHasPath();
|
||||
JsonPathValueAssert valueAssert = new JsonPathValueAssert(value, path, this.jsonMessageConverter);
|
||||
valueRequirements.accept(() -> valueAssert);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the given JSON {@code path} matches. For paths with an
|
||||
* operator, this validates that the path expression is valid, but does not
|
||||
* validate that it yield any results.
|
||||
* @param path the {@link JsonPath} expression
|
||||
*/
|
||||
public JsonPathAssert hasPath(String path) {
|
||||
new JsonPathValue(path).assertHasPath();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the given JSON {@code path} does not match.
|
||||
* @param path the {@link JsonPath} expression
|
||||
*/
|
||||
public JsonPathAssert doesNotHavePath(String path) {
|
||||
new JsonPathValue(path).assertDoesNotHavePath();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
private AssertionError failure(BasicErrorMessageFactory errorMessageFactory) {
|
||||
throw failures.failure(this.info, errorMessageFactory);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A {@link JsonPath} value.
|
||||
*/
|
||||
private class JsonPathValue {
|
||||
|
||||
private final String path;
|
||||
|
||||
private final JsonPath jsonPath;
|
||||
|
||||
private final String json;
|
||||
|
||||
JsonPathValue(String path) {
|
||||
Assert.hasText(path, "'path' must not be null or empty");
|
||||
this.path = path;
|
||||
this.jsonPath = JsonPath.compile(this.path);
|
||||
this.json = JsonPathAssert.this.actual.toString();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Object assertHasPath() {
|
||||
return getValue();
|
||||
}
|
||||
|
||||
void assertDoesNotHavePath() {
|
||||
try {
|
||||
read();
|
||||
throw failure(new JsonPathNotExpected(this.json, this.path));
|
||||
}
|
||||
catch (PathNotFoundException ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Object getValue() {
|
||||
try {
|
||||
return read();
|
||||
}
|
||||
catch (PathNotFoundException ex) {
|
||||
throw failure(new JsonPathNotFound(this.json, this.path));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object read() {
|
||||
return this.jsonPath.read(this.json);
|
||||
}
|
||||
|
||||
|
||||
static final class JsonPathNotFound extends BasicErrorMessageFactory {
|
||||
|
||||
private JsonPathNotFound(String actual, String path) {
|
||||
super("%nExpecting:%n %s%nTo match JSON path:%n %s%n", actual, path);
|
||||
}
|
||||
}
|
||||
|
||||
static final class JsonPathNotExpected extends BasicErrorMessageFactory {
|
||||
|
||||
private JsonPathNotExpected(String actual, String path) {
|
||||
super("%nExpecting:%n %s%nNot to match JSON path:%n %s%n", actual, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -28,7 +28,9 @@ import org.assertj.core.api.Assertions;
|
|||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.HttpStatus.Series;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.http.HttpHeadersAssert;
|
||||
import org.springframework.test.http.MediaTypeAssert;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.function.SingletonSupplier;
|
||||
|
|
@ -48,15 +50,24 @@ import org.springframework.util.function.SingletonSupplier;
|
|||
public abstract class AbstractHttpServletResponseAssert<R extends HttpServletResponse, SELF extends AbstractHttpServletResponseAssert<R, SELF, ACTUAL>, ACTUAL>
|
||||
extends AbstractObjectAssert<SELF, ACTUAL> {
|
||||
|
||||
private final Supplier<AbstractIntegerAssert<?>> statusAssert;
|
||||
private final Supplier<MediaTypeAssert> contentTypeAssertSupplier;
|
||||
|
||||
private final Supplier<HttpHeadersAssert> headersAssertSupplier;
|
||||
|
||||
private final Supplier<AbstractIntegerAssert<?>> statusAssert;
|
||||
|
||||
|
||||
protected AbstractHttpServletResponseAssert(ACTUAL actual, Class<?> selfType) {
|
||||
super(actual, selfType);
|
||||
this.statusAssert = SingletonSupplier.of(() -> Assertions.assertThat(getResponse().getStatus()).as("HTTP status code"));
|
||||
this.contentTypeAssertSupplier = SingletonSupplier.of(() -> new MediaTypeAssert(getResponse().getContentType()));
|
||||
this.headersAssertSupplier = SingletonSupplier.of(() -> new HttpHeadersAssert(getHttpHeaders(getResponse())));
|
||||
this.statusAssert = SingletonSupplier.of(() -> Assertions.assertThat(getResponse().getStatus()).as("HTTP status code"));
|
||||
}
|
||||
|
||||
private static HttpHeaders getHttpHeaders(HttpServletResponse response) {
|
||||
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
|
||||
response.getHeaderNames().forEach(name -> headers.put(name, new ArrayList<>(response.getHeaders(name))));
|
||||
return new HttpHeaders(headers);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -67,6 +78,14 @@ public abstract class AbstractHttpServletResponseAssert<R extends HttpServletRes
|
|||
*/
|
||||
protected abstract R getResponse();
|
||||
|
||||
/**
|
||||
* Return a new {@linkplain MediaTypeAssert assertion} object that uses the
|
||||
* response's {@linkplain MediaType content type} as the object to test.
|
||||
*/
|
||||
public MediaTypeAssert contentType() {
|
||||
return this.contentTypeAssertSupplier.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@linkplain HttpHeadersAssert assertion} object that uses
|
||||
* {@link HttpHeaders} as the object to test. The returned assertion
|
||||
|
|
@ -84,6 +103,82 @@ public abstract class AbstractHttpServletResponseAssert<R extends HttpServletRes
|
|||
return this.headersAssertSupplier.get();
|
||||
}
|
||||
|
||||
// Content-type shortcuts
|
||||
|
||||
/**
|
||||
* Verify that the response's {@code Content-Type} is equal to the given value.
|
||||
* @param contentType the expected content type
|
||||
*/
|
||||
public SELF hasContentType(MediaType contentType) {
|
||||
contentType().isEqualTo(contentType);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the response's {@code Content-Type} is equal to the given
|
||||
* string representation.
|
||||
* @param contentType the expected content type
|
||||
*/
|
||||
public SELF hasContentType(String contentType) {
|
||||
contentType().isEqualTo(contentType);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the response's {@code Content-Type} is
|
||||
* {@linkplain MediaType#isCompatibleWith(MediaType) compatible} with the
|
||||
* given value.
|
||||
* @param contentType the expected compatible content type
|
||||
*/
|
||||
public SELF hasContentTypeCompatibleWith(MediaType contentType) {
|
||||
contentType().isCompatibleWith(contentType);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the response's {@code Content-Type} is
|
||||
* {@linkplain MediaType#isCompatibleWith(MediaType) compatible} with the
|
||||
* given string representation.
|
||||
* @param contentType the expected compatible content type
|
||||
*/
|
||||
public SELF hasContentTypeCompatibleWith(String contentType) {
|
||||
contentType().isCompatibleWith(contentType);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
// Headers shortcuts
|
||||
|
||||
/**
|
||||
* Verify that the response contains a header with the given {@code name}.
|
||||
* @param name the name of an expected HTTP header
|
||||
*/
|
||||
public SELF containsHeader(String name) {
|
||||
headers().containsHeader(name);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the response does not contain a header with the given {@code name}.
|
||||
* @param name the name of an HTTP header that should not be present
|
||||
*/
|
||||
public SELF doesNotContainHeader(String name) {
|
||||
headers().doesNotContainHeader(name);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the response contains a header with the given {@code name}
|
||||
* and primary {@code value}.
|
||||
* @param name the name of an expected HTTP header
|
||||
* @param value the expected value of the header
|
||||
*/
|
||||
public SELF hasHeader(String name, String value) {
|
||||
headers().hasValue(name, value);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
// Status
|
||||
|
||||
/**
|
||||
* Verify that the HTTP status is equal to the specified status code.
|
||||
* @param status the expected HTTP status code
|
||||
|
|
@ -159,10 +254,4 @@ public abstract class AbstractHttpServletResponseAssert<R extends HttpServletRes
|
|||
return this.statusAssert.get();
|
||||
}
|
||||
|
||||
private static HttpHeaders getHttpHeaders(HttpServletResponse response) {
|
||||
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
|
||||
response.getHeaderNames().forEach(name -> headers.put(name, new ArrayList<>(response.getHeaders(name))));
|
||||
return new HttpHeaders(headers);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,16 @@ package org.springframework.test.web.servlet.assertj;
|
|||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.assertj.core.api.AbstractByteArrayAssert;
|
||||
import org.assertj.core.api.AbstractStringAssert;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.assertj.core.api.ByteArrayAssert;
|
||||
|
||||
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.JsonContentAssert;
|
||||
import org.springframework.test.web.UriAssert;
|
||||
|
||||
/**
|
||||
|
|
@ -45,22 +52,62 @@ public abstract class AbstractMockHttpServletResponseAssert<SELF extends Abstrac
|
|||
this.jsonMessageConverter = jsonMessageConverter;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a new {@linkplain ResponseBodyAssert assertion} object that uses
|
||||
* the response body as the object to test. The returned assertion object
|
||||
* provides access to the raw byte array, a String value decoded using the
|
||||
* response's character encoding, and dedicated JSON testing support.
|
||||
* Return a new {@linkplain AbstractStringAssert assertion} object that uses
|
||||
* the response body converted to text as the object to test.
|
||||
* <p>Examples: <pre><code class='java'>
|
||||
* // Check that the response body is equal to "Hello World":
|
||||
* assertThat(response).body().isEqualTo("Hello World");
|
||||
*
|
||||
* // Check that the response body is strictly equal to the content of "test.json":
|
||||
* assertThat(response).body().json().isStrictlyEqualToJson("test.json");
|
||||
* assertThat(response).bodyText().isEqualTo("Hello World");
|
||||
* </code></pre>
|
||||
*/
|
||||
public ResponseBodyAssert body() {
|
||||
return new ResponseBodyAssert(getResponse().getContentAsByteArray(),
|
||||
Charset.forName(getResponse().getCharacterEncoding()), this.jsonMessageConverter);
|
||||
public AbstractStringAssert<?> bodyText() {
|
||||
return Assertions.assertThat(readBody());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@linkplain AbstractJsonContentAssert assertion} object that
|
||||
* uses the response body converted to text as the object to test. Compared
|
||||
* to {@link #bodyText()}, the assertion object provides dedicated JSON
|
||||
* support.
|
||||
* <p>Examples: <pre><code class='java'>
|
||||
* // Check that the response body is strictly equal to the content of
|
||||
* // "/com/acme/sample/person-created.json":
|
||||
* assertThat(response).bodyJson()
|
||||
* .isStrictlyEqualToJson("/com/acme/sample/person-created.json");
|
||||
*
|
||||
* // Check that the response is strictly equal to the content of the
|
||||
* // specified file located in the same package as the PersonController:
|
||||
* assertThat(response).bodyJson().withResourceLoadClass(PersonController.class)
|
||||
* .isStrictlyEqualToJson("person-created.json");
|
||||
* </code></pre>
|
||||
* The returned assert object also supports JSON path expressions.
|
||||
* <p>Examples: <pre><code class='java'>
|
||||
* // Check that the JSON document does not have an "error" element
|
||||
* assertThat(response).bodyJson().doesNotHavePath("$.error");
|
||||
*
|
||||
* // Check that the JSON document as a top level "message" element
|
||||
* assertThat(response).bodyJson()
|
||||
* .extractingPath("$.message").asString().isEqualTo("hello");
|
||||
* </code></pre>
|
||||
*/
|
||||
public AbstractJsonContentAssert<?> bodyJson() {
|
||||
return new JsonContentAssert(readBody(), this.jsonMessageConverter);
|
||||
}
|
||||
|
||||
private String readBody() {
|
||||
return new String(getResponse().getContentAsByteArray(),
|
||||
Charset.forName(getResponse().getCharacterEncoding()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@linkplain AbstractByteArrayAssert assertion} object that
|
||||
* uses the response body as the object to test.
|
||||
* @see #bodyText()
|
||||
* @see #bodyJson()
|
||||
*/
|
||||
public AbstractByteArrayAssert<?> body() {
|
||||
return new ByteArrayAssert(getResponse().getContentAsByteArray());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -89,6 +136,14 @@ public abstract class AbstractMockHttpServletResponseAssert<SELF extends Abstrac
|
|||
return new UriAssert(getResponse().getRedirectedUrl(), "Redirected URL");
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the response body is equal to the given value.
|
||||
*/
|
||||
public SELF hasBodyTextEqualTo(String bodyText) {
|
||||
bodyText().isEqualTo(bodyText);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the forwarded URL is equal to the given value.
|
||||
* @param forwardedUrl the expected forwarded URL (can be null)
|
||||
|
|
|
|||
|
|
@ -21,20 +21,17 @@ import org.springframework.lang.Nullable;
|
|||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.web.servlet.FlashMap;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* The default {@link AssertableMvcResult} implementation.
|
||||
* The default {@link MvcTestResult} implementation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.2
|
||||
*/
|
||||
final class DefaultAssertableMvcResult implements AssertableMvcResult {
|
||||
final class DefaultMvcTestResult implements MvcTestResult {
|
||||
|
||||
@Nullable
|
||||
private final MvcResult target;
|
||||
private final MvcResult mvcResult;
|
||||
|
||||
@Nullable
|
||||
private final Exception unresolvedException;
|
||||
|
|
@ -42,86 +39,46 @@ final class DefaultAssertableMvcResult implements AssertableMvcResult {
|
|||
@Nullable
|
||||
private final GenericHttpMessageConverter<Object> jsonMessageConverter;
|
||||
|
||||
DefaultAssertableMvcResult(@Nullable MvcResult target, @Nullable Exception unresolvedException, @Nullable GenericHttpMessageConverter<Object> jsonMessageConverter) {
|
||||
this.target = target;
|
||||
DefaultMvcTestResult(@Nullable MvcResult mvcResult, @Nullable Exception unresolvedException, @Nullable GenericHttpMessageConverter<Object> jsonMessageConverter) {
|
||||
this.mvcResult = mvcResult;
|
||||
this.unresolvedException = unresolvedException;
|
||||
this.jsonMessageConverter = jsonMessageConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the exception that was thrown unexpectedly while processing the
|
||||
* request, if any.
|
||||
*/
|
||||
public MvcResult getMvcResult() {
|
||||
if (this.mvcResult == null) {
|
||||
throw new IllegalStateException(
|
||||
"Request has failed with unresolved exception " + this.unresolvedException);
|
||||
}
|
||||
return this.mvcResult;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Exception getUnresolvedException() {
|
||||
return this.unresolvedException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MockHttpServletRequest getRequest() {
|
||||
return getTarget().getRequest();
|
||||
return getMvcResult().getRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MockHttpServletResponse getResponse() {
|
||||
return getTarget().getResponse();
|
||||
return getMvcResult().getResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getHandler() {
|
||||
return getTarget().getHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public HandlerInterceptor[] getInterceptors() {
|
||||
return getTarget().getInterceptors();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public ModelAndView getModelAndView() {
|
||||
return getTarget().getModelAndView();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Exception getResolvedException() {
|
||||
return getTarget().getResolvedException();
|
||||
return getMvcResult().getResolvedException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FlashMap getFlashMap() {
|
||||
return getTarget().getFlashMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAsyncResult() {
|
||||
return getTarget().getAsyncResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAsyncResult(long timeToWait) {
|
||||
return getTarget().getAsyncResult(timeToWait);
|
||||
}
|
||||
|
||||
|
||||
private MvcResult getTarget() {
|
||||
if (this.target == null) {
|
||||
throw new IllegalStateException(
|
||||
"Request has failed with unresolved exception " + this.unresolvedException);
|
||||
}
|
||||
return this.target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use AssertJ's {@link org.assertj.core.api.Assertions#assertThat assertThat}
|
||||
* instead.
|
||||
*/
|
||||
@Override
|
||||
public MvcResultAssert assertThat() {
|
||||
return new MvcResultAssert(this, this.jsonMessageConverter);
|
||||
public MvcTestResultAssert assertThat() {
|
||||
return new MvcTestResultAssert(this, this.jsonMessageConverter);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -38,15 +38,46 @@ import org.springframework.util.Assert;
|
|||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
/**
|
||||
* {@link MockMvc} variant that tests Spring MVC exchanges and provides fluent
|
||||
* assertions using {@link org.assertj.core.api.Assertions AssertJ}.
|
||||
* Test Spring MVC applications with {@link MockMvc} for server request handling
|
||||
* using {@link org.assertj.core.api.Assertions AssertJ}.
|
||||
*
|
||||
* <p>A tester instance can be created from a {@link WebApplicationContext}:
|
||||
* <pre><code class='java'>
|
||||
* // Create an instance with default settings
|
||||
* MockMvcTester mvc = MockMvcTester.from(applicationContext);
|
||||
*
|
||||
* // Create an instance with a custom Filter
|
||||
* MockMvcTester mvc = MockMvcTester.from(applicationContext,
|
||||
* builder -> builder.addFilters(filter).build());
|
||||
* </code></pre>
|
||||
*
|
||||
* <p>A tester can be created standalone by providing the controller(s) to
|
||||
* include in a standalone setup:<pre><code class='java'>
|
||||
* // Create an instance for PersonController
|
||||
* MockMvcTester mvc = MockMvcTester.of(new PersonController());
|
||||
* </code></pre>
|
||||
*
|
||||
* <p>Once a test instance is available, you can perform requests in
|
||||
* a similar fashion as with {@link MockMvc}, and wrapping the result in
|
||||
* {@code assertThat} provides access to assertions. For instance:
|
||||
* <pre><code class='java'>
|
||||
* // perform a GET on /hi and assert the response body is equal to Hello
|
||||
* assertThat(mvc.perform(get("/hi")))
|
||||
* .hasStatusOk().hasBodyTextEqualTo("Hello");
|
||||
* </code></pre>
|
||||
*
|
||||
* <p>A main difference with {@link MockMvc} is that an unresolved exception
|
||||
* is not thrown directly. Rather an {@link AssertableMvcResult} is available
|
||||
* with an {@link AssertableMvcResult#getUnresolvedException() unresolved
|
||||
* exception}.
|
||||
* is not thrown directly. Rather an {@link MvcTestResult} is available
|
||||
* with an {@link MvcTestResult#getUnresolvedException() unresolved
|
||||
* exception}. You can assert that a request has failed unexpectedly:
|
||||
* <pre><code class='java'>
|
||||
* // perform a GET on /hi and assert the response body is equal to Hello
|
||||
* assertThat(mvc.perform(get("/boom")))
|
||||
* .hasUnresolvedException())
|
||||
* .withMessage("Test exception");
|
||||
* </code></pre>
|
||||
*
|
||||
* <p>{@link AssertableMockMvc} can be configured with a list of
|
||||
* <p>{@link MockMvcTester} can be configured with a list of
|
||||
* {@linkplain HttpMessageConverter message converters} to allow the response
|
||||
* body to be deserialized, rather than asserting on the raw values.
|
||||
*
|
||||
|
|
@ -54,7 +85,7 @@ import org.springframework.web.context.WebApplicationContext;
|
|||
* @author Brian Clozel
|
||||
* @since 6.2
|
||||
*/
|
||||
public final class AssertableMockMvc {
|
||||
public final class MockMvcTester {
|
||||
|
||||
private static final MediaType JSON = MediaType.APPLICATION_JSON;
|
||||
|
||||
|
|
@ -64,23 +95,23 @@ public final class AssertableMockMvc {
|
|||
private final GenericHttpMessageConverter<Object> jsonMessageConverter;
|
||||
|
||||
|
||||
private AssertableMockMvc(MockMvc mockMvc, @Nullable GenericHttpMessageConverter<Object> jsonMessageConverter) {
|
||||
private MockMvcTester(MockMvc mockMvc, @Nullable GenericHttpMessageConverter<Object> jsonMessageConverter) {
|
||||
Assert.notNull(mockMvc, "mockMVC should not be null");
|
||||
this.mockMvc = mockMvc;
|
||||
this.jsonMessageConverter = jsonMessageConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link AssertableMockMvc} instance that delegates to the given
|
||||
* Create a {@link MockMvcTester} instance that delegates to the given
|
||||
* {@link MockMvc} instance.
|
||||
* @param mockMvc the MockMvc instance to delegate calls to
|
||||
*/
|
||||
public static AssertableMockMvc create(MockMvc mockMvc) {
|
||||
return new AssertableMockMvc(mockMvc, null);
|
||||
public static MockMvcTester create(MockMvc mockMvc) {
|
||||
return new MockMvcTester(mockMvc, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link AssertableMockMvc} instance using the given, fully
|
||||
* Create an {@link MockMvcTester} instance using the given, fully
|
||||
* initialized (i.e., <em>refreshed</em>) {@link WebApplicationContext}. The
|
||||
* given {@code customizations} are applied to the {@link DefaultMockMvcBuilder}
|
||||
* that ultimately creates the underlying {@link MockMvc} instance.
|
||||
|
|
@ -92,7 +123,7 @@ public final class AssertableMockMvc {
|
|||
* instance based on a {@link DefaultMockMvcBuilder}.
|
||||
* @see MockMvcBuilders#webAppContextSetup(WebApplicationContext)
|
||||
*/
|
||||
public static AssertableMockMvc from(WebApplicationContext applicationContext,
|
||||
public static MockMvcTester from(WebApplicationContext applicationContext,
|
||||
Function<DefaultMockMvcBuilder, MockMvc> customizations) {
|
||||
|
||||
DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(applicationContext);
|
||||
|
|
@ -101,7 +132,7 @@ public final class AssertableMockMvc {
|
|||
}
|
||||
|
||||
/**
|
||||
* Shortcut to create an {@link AssertableMockMvc} instance using the given,
|
||||
* Shortcut to create an {@link MockMvcTester} instance using the given,
|
||||
* fully initialized (i.e., <em>refreshed</em>) {@link WebApplicationContext}.
|
||||
* <p>Consider using {@link #from(WebApplicationContext, Function)} if
|
||||
* further customization of the underlying {@link MockMvc} instance is
|
||||
|
|
@ -110,12 +141,12 @@ public final class AssertableMockMvc {
|
|||
* MVC infrastructure and application controllers from
|
||||
* @see MockMvcBuilders#webAppContextSetup(WebApplicationContext)
|
||||
*/
|
||||
public static AssertableMockMvc from(WebApplicationContext applicationContext) {
|
||||
public static MockMvcTester from(WebApplicationContext applicationContext) {
|
||||
return from(applicationContext, DefaultMockMvcBuilder::build);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link AssertableMockMvc} instance by registering one or more
|
||||
* Create an {@link MockMvcTester} instance by registering one or more
|
||||
* {@code @Controller} instances and configuring Spring MVC infrastructure
|
||||
* programmatically.
|
||||
* <p>This allows full control over the instantiation and initialization of
|
||||
|
|
@ -129,7 +160,7 @@ public final class AssertableMockMvc {
|
|||
* Spring MVC infrastructure
|
||||
* @see MockMvcBuilders#standaloneSetup(Object...)
|
||||
*/
|
||||
public static AssertableMockMvc of(Collection<?> controllers,
|
||||
public static MockMvcTester of(Collection<?> controllers,
|
||||
Function<StandaloneMockMvcBuilder, MockMvc> customizations) {
|
||||
|
||||
StandaloneMockMvcBuilder builder = MockMvcBuilders.standaloneSetup(controllers.toArray());
|
||||
|
|
@ -137,8 +168,8 @@ public final class AssertableMockMvc {
|
|||
}
|
||||
|
||||
/**
|
||||
* Shortcut to create an {@link AssertableMockMvc} instance by registering
|
||||
* one or more {@code @Controller} instances.
|
||||
* Shortcut to create an {@link MockMvcTester} instance by registering one
|
||||
* or more {@code @Controller} instances.
|
||||
* <p>The minimum infrastructure required by the
|
||||
* {@link org.springframework.web.servlet.DispatcherServlet DispatcherServlet}
|
||||
* to serve requests with annotated controllers is created. Consider using
|
||||
|
|
@ -149,12 +180,12 @@ public final class AssertableMockMvc {
|
|||
* into an instance
|
||||
* @see MockMvcBuilders#standaloneSetup(Object...)
|
||||
*/
|
||||
public static AssertableMockMvc of(Object... controllers) {
|
||||
public static MockMvcTester of(Object... controllers) {
|
||||
return of(Arrays.asList(controllers), StandaloneMockMvcBuilder::build);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link AssertableMockMvc} instance using the specified
|
||||
* Return a new {@link MockMvcTester} instance using the specified
|
||||
* {@linkplain HttpMessageConverter message converters}.
|
||||
* <p>If none are specified, only basic assertions on the response body can
|
||||
* be performed. Consider registering a suitable JSON converter for asserting
|
||||
|
|
@ -162,13 +193,13 @@ public final class AssertableMockMvc {
|
|||
* @param httpMessageConverters the message converters to use
|
||||
* @return a new instance using the specified converters
|
||||
*/
|
||||
public AssertableMockMvc withHttpMessageConverters(Iterable<HttpMessageConverter<?>> httpMessageConverters) {
|
||||
return new AssertableMockMvc(this.mockMvc, findJsonMessageConverter(httpMessageConverters));
|
||||
public MockMvcTester withHttpMessageConverters(Iterable<HttpMessageConverter<?>> httpMessageConverters) {
|
||||
return new MockMvcTester(this.mockMvc, findJsonMessageConverter(httpMessageConverters));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a request and return a type that can be used with standard
|
||||
* {@link org.assertj.core.api.Assertions AssertJ} assertions.
|
||||
* Perform a request and return a {@link MvcTestResult result} that can be
|
||||
* used with standard {@link org.assertj.core.api.Assertions AssertJ} assertions.
|
||||
* <p>Use static methods of {@link MockMvcRequestBuilders} to prepare the
|
||||
* request, wrapping the invocation in {@code assertThat}. The following
|
||||
* asserts that a {@linkplain MockMvcRequestBuilders#get(URI) GET} request
|
||||
|
|
@ -191,16 +222,16 @@ public final class AssertableMockMvc {
|
|||
* @param requestBuilder used to prepare the request to execute;
|
||||
* see static factory methods in
|
||||
* {@link org.springframework.test.web.servlet.request.MockMvcRequestBuilders}
|
||||
* @return an {@link AssertableMvcResult} to be wrapped in {@code assertThat}
|
||||
* @return an {@link MvcTestResult} to be wrapped in {@code assertThat}
|
||||
* @see MockMvc#perform(RequestBuilder)
|
||||
*/
|
||||
public AssertableMvcResult perform(RequestBuilder requestBuilder) {
|
||||
public MvcTestResult perform(RequestBuilder requestBuilder) {
|
||||
Object result = getMvcResultOrFailure(requestBuilder);
|
||||
if (result instanceof MvcResult mvcResult) {
|
||||
return new DefaultAssertableMvcResult(mvcResult, null, this.jsonMessageConverter);
|
||||
return new DefaultMvcTestResult(mvcResult, null, this.jsonMessageConverter);
|
||||
}
|
||||
else {
|
||||
return new DefaultAssertableMvcResult(null, (Exception) result, this.jsonMessageConverter);
|
||||
return new DefaultMvcTestResult(null, (Exception) result, this.jsonMessageConverter);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -22,23 +22,34 @@ import org.springframework.lang.Nullable;
|
|||
import org.springframework.test.web.servlet.MvcResult;
|
||||
|
||||
/**
|
||||
* A {@link MvcResult} that additionally supports AssertJ style assertions.
|
||||
* Provide to the result of an executed request using {@link MockMvcTester} that
|
||||
* is meant to be used with {@link org.assertj.core.api.Assertions#assertThat(AssertProvider)
|
||||
* assertThat}.
|
||||
*
|
||||
* <p>Can be in one of two distinct states:
|
||||
* <ol>
|
||||
* <li>The request processed successfully, and {@link #getUnresolvedException()}
|
||||
* is therefore {@code null}.</li>
|
||||
* <li>The request processed successfully, even if it fails with an exception
|
||||
* that has been resolved. {@link #getMvcResult()} is available and
|
||||
* {@link #getUnresolvedException()} is {@code null}.</li>
|
||||
* <li>The request failed unexpectedly with {@link #getUnresolvedException()}
|
||||
* providing more information about the error. Any attempt to access a member of
|
||||
* the result fails with an exception.</li>
|
||||
* providing more information about the error. Any attempt to access
|
||||
* {@link #getMvcResult() the result } fails with an exception.</li>
|
||||
* </ol>
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Brian Clozel
|
||||
* @since 6.2
|
||||
* @see AssertableMockMvc
|
||||
* @see MockMvcTester
|
||||
*/
|
||||
public interface AssertableMvcResult extends MvcResult, AssertProvider<MvcResultAssert> {
|
||||
public interface MvcTestResult extends AssertProvider<MvcTestResultAssert> {
|
||||
|
||||
/**
|
||||
* Return the {@link MvcResult result} of the processing.
|
||||
* <p>If the request has failed unexpectedly, this throws an
|
||||
* {@link IllegalStateException}.
|
||||
* @return the {@link MvcResult}
|
||||
*/
|
||||
MvcResult getMvcResult();
|
||||
|
||||
/**
|
||||
* Return the exception that was thrown unexpectedly while processing the
|
||||
|
|
@ -30,12 +30,10 @@ import org.assertj.core.api.ObjectAssert;
|
|||
import org.assertj.core.error.BasicErrorMessageFactory;
|
||||
import org.assertj.core.internal.Failures;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.GenericHttpMessageConverter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.test.http.MediaTypeAssert;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.ResultHandler;
|
||||
import org.springframework.test.web.servlet.ResultMatcher;
|
||||
|
|
@ -43,22 +41,22 @@ import org.springframework.web.servlet.ModelAndView;
|
|||
|
||||
/**
|
||||
* AssertJ {@link org.assertj.core.api.Assert assertions} that can be applied
|
||||
* to {@link MvcResult}.
|
||||
* to {@link MvcTestResult}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Brian Clozel
|
||||
* @since 6.2
|
||||
*/
|
||||
public class MvcResultAssert extends AbstractMockHttpServletResponseAssert<MvcResultAssert, AssertableMvcResult> {
|
||||
public class MvcTestResultAssert extends AbstractMockHttpServletResponseAssert<MvcTestResultAssert, MvcTestResult> {
|
||||
|
||||
MvcResultAssert(AssertableMvcResult mvcResult, @Nullable GenericHttpMessageConverter<Object> jsonMessageConverter) {
|
||||
super(jsonMessageConverter, mvcResult, MvcResultAssert.class);
|
||||
MvcTestResultAssert(MvcTestResult actual, @Nullable GenericHttpMessageConverter<Object> jsonMessageConverter) {
|
||||
super(jsonMessageConverter, actual, MvcTestResultAssert.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MockHttpServletResponse getResponse() {
|
||||
checkHasNotFailedUnexpectedly();
|
||||
return this.actual.getResponse();
|
||||
getMvcResult();
|
||||
return this.actual.getMvcResult().getResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -76,8 +74,7 @@ public class MvcResultAssert extends AbstractMockHttpServletResponseAssert<MvcRe
|
|||
* object that uses the {@link MockHttpServletRequest} as the object to test.
|
||||
*/
|
||||
public AbstractMockHttpServletRequestAssert<?> request() {
|
||||
checkHasNotFailedUnexpectedly();
|
||||
return new MockHttpRequestAssert(this.actual.getRequest());
|
||||
return new MockHttpRequestAssert(getMvcResult().getRequest());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -85,17 +82,7 @@ public class MvcResultAssert extends AbstractMockHttpServletResponseAssert<MvcRe
|
|||
* response's {@linkplain Cookie cookies} as the object to test.
|
||||
*/
|
||||
public CookieMapAssert cookies() {
|
||||
checkHasNotFailedUnexpectedly();
|
||||
return new CookieMapAssert(this.actual.getResponse().getCookies());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@linkplain MediaTypeAssert assertion} object that uses the
|
||||
* response's {@linkplain MediaType content type} as the object to test.
|
||||
*/
|
||||
public MediaTypeAssert contentType() {
|
||||
checkHasNotFailedUnexpectedly();
|
||||
return new MediaTypeAssert(this.actual.getResponse().getContentType());
|
||||
return new CookieMapAssert(getMvcResult().getResponse().getCookies());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -108,8 +95,7 @@ public class MvcResultAssert extends AbstractMockHttpServletResponseAssert<MvcRe
|
|||
* </code></pre>
|
||||
*/
|
||||
public HandlerResultAssert handler() {
|
||||
checkHasNotFailedUnexpectedly();
|
||||
return new HandlerResultAssert(this.actual.getHandler());
|
||||
return new HandlerResultAssert(getMvcResult().getHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -118,7 +104,6 @@ public class MvcResultAssert extends AbstractMockHttpServletResponseAssert<MvcRe
|
|||
* {@linkplain ModelAndView#getModel() model} as the object to test.
|
||||
*/
|
||||
public ModelAssert model() {
|
||||
checkHasNotFailedUnexpectedly();
|
||||
return new ModelAssert(getModelAndView().getModel());
|
||||
}
|
||||
|
||||
|
|
@ -129,7 +114,6 @@ public class MvcResultAssert extends AbstractMockHttpServletResponseAssert<MvcRe
|
|||
* @see #hasViewName(String)
|
||||
*/
|
||||
public AbstractStringAssert<?> viewName() {
|
||||
checkHasNotFailedUnexpectedly();
|
||||
return Assertions.assertThat(getModelAndView().getViewName()).as("View name");
|
||||
}
|
||||
|
||||
|
|
@ -139,8 +123,7 @@ public class MvcResultAssert extends AbstractMockHttpServletResponseAssert<MvcRe
|
|||
* to test.
|
||||
*/
|
||||
public MapAssert<String, Object> flash() {
|
||||
checkHasNotFailedUnexpectedly();
|
||||
return new MapAssert<>(this.actual.getFlashMap());
|
||||
return new MapAssert<>(getMvcResult().getFlashMap());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -151,14 +134,14 @@ public class MvcResultAssert extends AbstractMockHttpServletResponseAssert<MvcRe
|
|||
*/
|
||||
public ObjectAssert<Object> asyncResult() {
|
||||
request().hasAsyncStarted(true);
|
||||
return Assertions.assertThat(this.actual.getAsyncResult()).as("Async result");
|
||||
return Assertions.assertThat(getMvcResult().getAsyncResult()).as("Async result");
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the request has failed with an unresolved exception.
|
||||
* @see #unresolvedException()
|
||||
*/
|
||||
public MvcResultAssert hasUnresolvedException() {
|
||||
public MvcTestResultAssert hasUnresolvedException() {
|
||||
Assertions.assertThat(this.actual.getUnresolvedException())
|
||||
.withFailMessage("Expecting request to have failed but it has succeeded").isNotNull();
|
||||
return this;
|
||||
|
|
@ -167,7 +150,7 @@ public class MvcResultAssert extends AbstractMockHttpServletResponseAssert<MvcRe
|
|||
/**
|
||||
* Verify that the request has not failed with an unresolved exception.
|
||||
*/
|
||||
public MvcResultAssert doesNotHaveUnresolvedException() {
|
||||
public MvcTestResultAssert doesNotHaveUnresolvedException() {
|
||||
Assertions.assertThat(this.actual.getUnresolvedException())
|
||||
.withFailMessage("Expecting request to have succeeded but it has failed").isNull();
|
||||
return this;
|
||||
|
|
@ -177,18 +160,18 @@ public class MvcResultAssert extends AbstractMockHttpServletResponseAssert<MvcRe
|
|||
* Verify that the actual mvc result matches the given {@link ResultMatcher}.
|
||||
* @param resultMatcher the result matcher to invoke
|
||||
*/
|
||||
public MvcResultAssert matches(ResultMatcher resultMatcher) {
|
||||
checkHasNotFailedUnexpectedly();
|
||||
return super.satisfies(resultMatcher::match);
|
||||
public MvcTestResultAssert matches(ResultMatcher resultMatcher) {
|
||||
MvcResult mvcResult = getMvcResult();
|
||||
return super.satisfies(tmc -> resultMatcher.match(mvcResult));
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the given {@link ResultHandler} to the actual mvc result.
|
||||
* @param resultHandler the result matcher to invoke
|
||||
*/
|
||||
public MvcResultAssert apply(ResultHandler resultHandler) {
|
||||
checkHasNotFailedUnexpectedly();
|
||||
return satisfies(resultHandler::handle);
|
||||
public MvcTestResultAssert apply(ResultHandler resultHandler) {
|
||||
MvcResult mvcResult = getMvcResult();
|
||||
return satisfies(tmc -> resultHandler.handle(mvcResult));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -197,7 +180,7 @@ public class MvcResultAssert extends AbstractMockHttpServletResponseAssert<MvcRe
|
|||
* {@link #viewName()}
|
||||
* @param viewName the expected view name
|
||||
*/
|
||||
public MvcResultAssert hasViewName(String viewName) {
|
||||
public MvcTestResultAssert hasViewName(String viewName) {
|
||||
viewName().isEqualTo(viewName);
|
||||
return this.myself;
|
||||
}
|
||||
|
|
@ -205,17 +188,18 @@ public class MvcResultAssert extends AbstractMockHttpServletResponseAssert<MvcRe
|
|||
|
||||
@SuppressWarnings("NullAway")
|
||||
private ModelAndView getModelAndView() {
|
||||
ModelAndView modelAndView = this.actual.getModelAndView();
|
||||
ModelAndView modelAndView = getMvcResult().getModelAndView();
|
||||
Assertions.assertThat(modelAndView).as("ModelAndView").isNotNull();
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
protected void checkHasNotFailedUnexpectedly() {
|
||||
protected MvcResult getMvcResult() {
|
||||
Exception unresolvedException = this.actual.getUnresolvedException();
|
||||
if (unresolvedException != null) {
|
||||
throw Failures.instance().failure(this.info,
|
||||
new RequestFailedUnexpectedly(unresolvedException));
|
||||
}
|
||||
return this.actual.getMvcResult();
|
||||
}
|
||||
|
||||
private static final class MockHttpRequestAssert extends AbstractMockHttpServletRequestAssert<MockHttpRequestAssert> {
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.web.servlet.assertj;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.assertj.core.api.AbstractByteArrayAssert;
|
||||
import org.assertj.core.api.AbstractStringAssert;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.http.converter.GenericHttpMessageConverter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.json.JsonContentAssert;
|
||||
import org.springframework.test.json.JsonPathAssert;
|
||||
|
||||
/**
|
||||
* AssertJ {@link org.assertj.core.api.Assert assertions} that can be applied to
|
||||
* the response body.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Brian Clozel
|
||||
* @since 6.2
|
||||
*/
|
||||
public class ResponseBodyAssert extends AbstractByteArrayAssert<ResponseBodyAssert> {
|
||||
|
||||
private final Charset characterEncoding;
|
||||
|
||||
@Nullable
|
||||
private final GenericHttpMessageConverter<Object> jsonMessageConverter;
|
||||
|
||||
ResponseBodyAssert(byte[] actual, Charset characterEncoding,
|
||||
@Nullable GenericHttpMessageConverter<Object> jsonMessageConverter) {
|
||||
|
||||
super(actual, ResponseBodyAssert.class);
|
||||
this.characterEncoding = characterEncoding;
|
||||
this.jsonMessageConverter = jsonMessageConverter;
|
||||
as("Response body");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@linkplain JsonPathAssert assertion} object that provides
|
||||
* {@linkplain com.jayway.jsonpath.JsonPath JSON path} assertions on the
|
||||
* response body.
|
||||
*/
|
||||
public JsonPathAssert jsonPath() {
|
||||
return new JsonPathAssert(getJson(), this.jsonMessageConverter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@linkplain JsonContentAssert assertion} object that provides
|
||||
* support for {@linkplain org.skyscreamer.jsonassert.JSONCompareMode JSON
|
||||
* assert} comparisons against expected JSON input which can be loaded from
|
||||
* the classpath.
|
||||
* <p>This method only supports absolute locations for JSON documents loaded
|
||||
* from the classpath. Consider using {@link #json(Class)} to load JSON
|
||||
* documents relative to a given class.
|
||||
* <p>Example: <pre><code class='java'>
|
||||
* // Check that the response is strictly equal to the content of
|
||||
* // "/com/acme/web/person/person-created.json":
|
||||
* assertThat(...).body().json()
|
||||
* .isStrictlyEqualToJson("/com/acme/web/person/person-created.json");
|
||||
* </code></pre>
|
||||
*/
|
||||
public JsonContentAssert json() {
|
||||
return json(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@linkplain JsonContentAssert assertion} object that provides
|
||||
* support for {@linkplain org.skyscreamer.jsonassert.JSONCompareMode JSON
|
||||
* assert} comparisons against expected JSON input which can be loaded from
|
||||
* the classpath.
|
||||
* <p>Locations for JSON documents can be absolute using a leading slash, or
|
||||
* relative to the given {@code resourceLoadClass}.
|
||||
* <p>Example: <pre><code class='java'>
|
||||
* // Check that the response is strictly equal to the content of the
|
||||
* // specified file located in the same package as the PersonController:
|
||||
* assertThat(...).body().json(PersonController.class)
|
||||
* .isStrictlyEqualToJson("person-created.json");
|
||||
* </code></pre>
|
||||
* @param resourceLoadClass the class used to load relative JSON documents
|
||||
* @see ClassPathResource#ClassPathResource(String, Class)
|
||||
*/
|
||||
public JsonContentAssert json(@Nullable Class<?> resourceLoadClass) {
|
||||
return new JsonContentAssert(getJson(), resourceLoadClass, this.characterEncoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the response body is equal to the given {@link String}.
|
||||
* <p>Converts the actual byte array to a String using the character encoding
|
||||
* of the {@link HttpServletResponse}.
|
||||
* @param expected the expected content of the response body
|
||||
* @see #asString()
|
||||
*/
|
||||
public ResponseBodyAssert isEqualTo(String expected) {
|
||||
asString().isEqualTo(expected);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override that uses the character encoding of the {@link HttpServletResponse}
|
||||
* to convert the byte[] to a String, rather than the platform's default charset.
|
||||
*/
|
||||
@Override
|
||||
public AbstractStringAssert<?> asString() {
|
||||
return asString(this.characterEncoding);
|
||||
}
|
||||
|
||||
private String getJson() {
|
||||
return new String(this.actual, this.characterEncoding);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,785 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.json;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.assertj.core.api.AssertProvider;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.api.TestInstance.Lifecycle;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.skyscreamer.jsonassert.JSONCompareMode;
|
||||
import org.skyscreamer.jsonassert.comparator.DefaultComparator;
|
||||
import org.skyscreamer.jsonassert.comparator.JSONComparator;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.converter.GenericHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
|
||||
/**
|
||||
* Tests for {@link AbstractJsonContentAssert}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class AbstractJsonContentAssertTests {
|
||||
|
||||
private static final String TYPES = loadJson("types.json");
|
||||
|
||||
private static final String SIMPSONS = loadJson("simpsons.json");
|
||||
|
||||
private static final String NULLS = loadJson("nulls.json");
|
||||
|
||||
private static final String SOURCE = loadJson("source.json");
|
||||
|
||||
private static final String LENIENT_SAME = loadJson("lenient-same.json");
|
||||
|
||||
private static final String DIFFERENT = loadJson("different.json");
|
||||
|
||||
private static final MappingJackson2HttpMessageConverter jsonHttpMessageConverter =
|
||||
new MappingJackson2HttpMessageConverter(new ObjectMapper());
|
||||
|
||||
private static final JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT);
|
||||
|
||||
@Test
|
||||
void isNullWhenActualIsNullShouldPass() {
|
||||
assertThat(forJson(null)).isNull();
|
||||
}
|
||||
|
||||
@Nested
|
||||
class HasPathTests {
|
||||
|
||||
@Test
|
||||
void hasPathForNullJson() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(null)).hasPath("no"))
|
||||
.withMessageContaining("Expecting actual not to be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasPathForPresentAndNotNull() {
|
||||
assertThat(forJson(NULLS)).hasPath("$.valuename");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasPathForPresentAndNull() {
|
||||
assertThat(forJson(NULLS)).hasPath("$.nullname");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasPathForOperatorMatching() {
|
||||
assertThat(forJson(SIMPSONS)).
|
||||
hasPath("$.familyMembers[?(@.name == 'Homer')]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasPathForOperatorNotMatching() {
|
||||
assertThat(forJson(SIMPSONS)).
|
||||
hasPath("$.familyMembers[?(@.name == 'Dilbert')]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasPathForNotPresent() {
|
||||
String expression = "$.missing";
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(NULLS)).hasPath(expression))
|
||||
.satisfies(hasFailedToMatchPath("$.missing"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasPathSatisfying() {
|
||||
assertThat(forJson(TYPES)).hasPathSatisfying("$.str", value -> assertThat(value).isEqualTo("foo"))
|
||||
.hasPathSatisfying("$.num", value -> assertThat(value).isEqualTo(5));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasPathSatisfyingForPathNotPresent() {
|
||||
String expression = "missing";
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(NULLS)).hasPathSatisfying(expression, value -> {}))
|
||||
.satisfies(hasFailedToMatchPath(expression));
|
||||
}
|
||||
|
||||
@Test
|
||||
void doesNotHavePathForMissing() {
|
||||
assertThat(forJson(NULLS)).doesNotHavePath("$.missing");
|
||||
}
|
||||
|
||||
@Test
|
||||
void doesNotHavePathForPresent() {
|
||||
String expression = "$.valuename";
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(NULLS)).doesNotHavePath(expression))
|
||||
.satisfies(hasFailedToNotMatchPath(expression));
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ExtractingPathTests {
|
||||
|
||||
@Test
|
||||
void extractingPathForNullJson() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(null)).extractingPath("$"))
|
||||
.withMessageContaining("Expecting actual not to be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNullWithNullPathValue() {
|
||||
assertThat(forJson(NULLS)).extractingPath("$.nullname").isNull();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "$.str", "$.emptyString", "$.num", "$.bool", "$.arr",
|
||||
"$.emptyArray", "$.colorMap", "$.emptyMap" })
|
||||
void isNotNullWithValue(String path) {
|
||||
assertThat(forJson(TYPES)).extractingPath(path).isNotNull();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void isEqualToOnRawValue(String path, Object expected) {
|
||||
assertThat(forJson(TYPES)).extractingPath(path).isEqualTo(expected);
|
||||
}
|
||||
|
||||
static Stream<Arguments> isEqualToOnRawValue() {
|
||||
return Stream.of(
|
||||
Arguments.of("$.str", "foo"),
|
||||
Arguments.of("$.num", 5),
|
||||
Arguments.of("$.bool", true),
|
||||
Arguments.of("$.arr", List.of(42)),
|
||||
Arguments.of("$.colorMap", Map.of("red", "rojo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void asStringWithActualValue() {
|
||||
assertThat(forJson(TYPES)).extractingPath("@.str").asString().startsWith("f").endsWith("o");
|
||||
}
|
||||
|
||||
@Test
|
||||
void asStringIsEmpty() {
|
||||
assertThat(forJson(TYPES)).extractingPath("@.emptyString").asString().isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void asNumberWithActualValue() {
|
||||
assertThat(forJson(TYPES)).extractingPath("@.num").asNumber().isEqualTo(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
void asBooleanWithActualValue() {
|
||||
assertThat(forJson(TYPES)).extractingPath("@.bool").asBoolean().isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void asArrayWithActualValue() {
|
||||
assertThat(forJson(TYPES)).extractingPath("@.arr").asArray().containsOnly(42);
|
||||
}
|
||||
|
||||
@Test
|
||||
void asArrayIsEmpty() {
|
||||
assertThat(forJson(TYPES)).extractingPath("@.emptyArray").asArray().isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void asArrayWithFilterPredicatesMatching() {
|
||||
assertThat(forJson(SIMPSONS))
|
||||
.extractingPath("$.familyMembers[?(@.name == 'Bart')]").asArray().hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void asArrayWithFilterPredicatesNotMatching() {
|
||||
assertThat(forJson(SIMPSONS)).
|
||||
extractingPath("$.familyMembers[?(@.name == 'Dilbert')]").asArray().isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void asMapWithActualValue() {
|
||||
assertThat(forJson(TYPES)).extractingPath("@.colorMap").asMap().containsOnly(entry("red", "rojo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void asMapIsEmpty() {
|
||||
assertThat(forJson(TYPES)).extractingPath("@.emptyMap").asMap().isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void convertToWithoutHttpMessageConverterShouldFail() {
|
||||
JsonPathValueAssert path = assertThat(forJson(SIMPSONS)).extractingPath("$.familyMembers[0]");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> path.convertTo(ExtractingPathTests.Member.class))
|
||||
.withMessage("No JSON message converter available to convert {name=Homer}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void convertToTargetType() {
|
||||
assertThat(forJson(SIMPSONS, jsonHttpMessageConverter))
|
||||
.extractingPath("$.familyMembers[0]").convertTo(ExtractingPathTests.Member.class)
|
||||
.satisfies(member -> assertThat(member.name).isEqualTo("Homer"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void convertToIncompatibleTargetTypeShouldFail() {
|
||||
JsonPathValueAssert path = assertThat(forJson(SIMPSONS, jsonHttpMessageConverter))
|
||||
.extractingPath("$.familyMembers[0]");
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> path.convertTo(ExtractingPathTests.Customer.class))
|
||||
.withMessageContainingAll("Expected value at JSON path \"$.familyMembers[0]\":",
|
||||
Customer.class.getName(), "name");
|
||||
}
|
||||
|
||||
@Test
|
||||
void convertArrayToParameterizedType() {
|
||||
assertThat(forJson(SIMPSONS, jsonHttpMessageConverter))
|
||||
.extractingPath("$.familyMembers")
|
||||
.convertTo(new ParameterizedTypeReference<List<Member>>() {})
|
||||
.satisfies(family -> assertThat(family).hasSize(5).element(0).isEqualTo(new Member("Homer")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEmptyWithPathHavingNullValue() {
|
||||
assertThat(forJson(NULLS)).extractingPath("nullname").isEmpty();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "$.emptyString", "$.emptyArray", "$.emptyMap" })
|
||||
void isEmptyWithEmptyValue(String path) {
|
||||
assertThat(forJson(TYPES)).extractingPath(path).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEmptyForPathWithFilterMatching() {
|
||||
String expression = "$.familyMembers[?(@.name == 'Bart')]";
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SIMPSONS)).extractingPath(expression).isEmpty())
|
||||
.withMessageContainingAll("Expected value at JSON path \"" + expression + "\"",
|
||||
"[{\"name\":\"Bart\"}]", "To be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEmptyForPathWithFilterNotMatching() {
|
||||
assertThat(forJson(SIMPSONS)).extractingPath("$.familyMembers[?(@.name == 'Dilbert')]").isEmpty();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "$.str", "$.num", "$.bool", "$.arr", "$.colorMap" })
|
||||
void isNotEmptyWithNonNullValue(String path) {
|
||||
assertThat(forJson(TYPES)).extractingPath(path).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEmptyForPathWithFilterMatching() {
|
||||
assertThat(forJson(SIMPSONS)).extractingPath("$.familyMembers[?(@.name == 'Bart')]").isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEmptyForPathWithFilterNotMatching() {
|
||||
String expression = "$.familyMembers[?(@.name == 'Dilbert')]";
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SIMPSONS)).extractingPath(expression).isNotEmpty())
|
||||
.withMessageContainingAll("Expected value at JSON path \"" + expression + "\"",
|
||||
"To not be empty");
|
||||
}
|
||||
|
||||
|
||||
private record Member(String name) {}
|
||||
|
||||
private record Customer(long id, String username) {}
|
||||
|
||||
private AssertProvider<AbstractJsonContentAssert<?>> forJson(@Nullable String json) {
|
||||
return () -> new TestJsonContentAssert(json, null);
|
||||
}
|
||||
|
||||
private AssertProvider<AbstractJsonContentAssert<?>> forJson(@Nullable String json, GenericHttpMessageConverter<Object> jsonHttpMessageConverter) {
|
||||
return () -> new TestJsonContentAssert(json, jsonHttpMessageConverter);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class EqualsNotEqualsTests {
|
||||
|
||||
@Test
|
||||
void isEqualToWhenStringIsMatchingShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isEqualTo(SOURCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenNullActualShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(() ->
|
||||
assertThat(forJson(null)).isEqualTo(SOURCE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenExpectedIsNotAStringShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isEqualTo(SOURCE.getBytes()));
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(Lifecycle.PER_CLASS)
|
||||
class JsonAssertTests {
|
||||
|
||||
@Test
|
||||
void isEqualToWhenExpectedIsNullShouldFail() {
|
||||
CharSequence actual = null;
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isEqualTo(actual, JSONCompareMode.LENIENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenStringIsMatchingAndLenientShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isEqualTo(LENIENT_SAME, JSONCompareMode.LENIENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenStringIsNotMatchingAndLenientShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isEqualTo(DIFFERENT, JSONCompareMode.LENIENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenResourcePathIsMatchingAndLenientShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isEqualTo("lenient-same.json", JSONCompareMode.LENIENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenResourcePathIsNotMatchingAndLenientShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isEqualTo("different.json", JSONCompareMode.LENIENT));
|
||||
}
|
||||
|
||||
Stream<Arguments> source() {
|
||||
return Stream.of(
|
||||
Arguments.of(new ClassPathResource("source.json", AbstractJsonContentAssertTests.class)),
|
||||
Arguments.of(new ByteArrayResource(SOURCE.getBytes())),
|
||||
Arguments.of(new FileSystemResource(createFile(SOURCE))),
|
||||
Arguments.of(new InputStreamResource(createInputStream(SOURCE))));
|
||||
}
|
||||
|
||||
Stream<Arguments> lenientSame() {
|
||||
return Stream.of(
|
||||
Arguments.of(new ClassPathResource("lenient-same.json", AbstractJsonContentAssertTests.class)),
|
||||
Arguments.of(new ByteArrayResource(LENIENT_SAME.getBytes())),
|
||||
Arguments.of(new FileSystemResource(createFile(LENIENT_SAME))),
|
||||
Arguments.of(new InputStreamResource(createInputStream(LENIENT_SAME))));
|
||||
}
|
||||
|
||||
Stream<Arguments> different() {
|
||||
return Stream.of(
|
||||
Arguments.of(new ClassPathResource("different.json", AbstractJsonContentAssertTests.class)),
|
||||
Arguments.of(new ByteArrayResource(DIFFERENT.getBytes())),
|
||||
Arguments.of(new FileSystemResource(createFile(DIFFERENT))),
|
||||
Arguments.of(new InputStreamResource(createInputStream(DIFFERENT))));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("lenientSame")
|
||||
void isEqualToWhenResourceIsMatchingAndLenientSameShouldPass(Resource expected) {
|
||||
assertThat(forJson(SOURCE)).isEqualTo(expected, JSONCompareMode.LENIENT);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("different")
|
||||
void isEqualToWhenResourceIsNotMatchingAndLenientShouldFail(Resource expected) {
|
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(
|
||||
() -> assertThat(forJson(SOURCE)).isEqualTo(expected, JSONCompareMode.LENIENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenStringIsMatchingAndComparatorShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isEqualTo(LENIENT_SAME, comparator);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenStringIsNotMatchingAndComparatorShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isEqualTo(DIFFERENT, comparator));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenResourcePathIsMatchingAndComparatorShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isEqualTo("lenient-same.json", comparator);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenResourcePathIsNotMatchingAndComparatorShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isEqualTo("different.json", comparator));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("lenientSame")
|
||||
void isEqualToWhenResourceIsMatchingAndComparatorShouldPass(Resource expected) {
|
||||
assertThat(forJson(SOURCE)).isEqualTo(expected, comparator);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("different")
|
||||
void isEqualToWhenResourceIsNotMatchingAndComparatorShouldFail(Resource expected) {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isEqualTo(expected, comparator));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isLenientlyEqualToWhenStringIsMatchingShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isLenientlyEqualTo(LENIENT_SAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isLenientlyEqualToWhenNullActualShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(null)).isLenientlyEqualTo(SOURCE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isLenientlyEqualToWhenStringIsNotMatchingShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isLenientlyEqualTo(DIFFERENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isLenientlyEqualToWhenExpectedDoesNotExistShouldFail() {
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isLenientlyEqualTo("does-not-exist.json"))
|
||||
.withMessage("Unable to load JSON from class path resource [org/springframework/test/json/does-not-exist.json]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void isLenientlyEqualToWhenResourcePathIsMatchingShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isLenientlyEqualTo("lenient-same.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void isLenientlyEqualToWhenResourcePathIsNotMatchingShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isLenientlyEqualTo("different.json"));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("lenientSame")
|
||||
void isLenientlyEqualToWhenResourceIsMatchingShouldPass(Resource expected) {
|
||||
assertThat(forJson(SOURCE)).isLenientlyEqualTo(expected);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("different")
|
||||
void isLenientlyEqualToWhenResourceIsNotMatchingShouldFail(Resource expected) {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isLenientlyEqualTo(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isStrictlyEqualToWhenStringIsMatchingShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isStrictlyEqualTo(SOURCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isStrictlyEqualToWhenStringIsNotMatchingShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isStrictlyEqualTo(LENIENT_SAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isStrictlyEqualToWhenResourcePathIsMatchingShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isStrictlyEqualTo("source.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void isStrictlyEqualToWhenResourcePathIsNotMatchingShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isStrictlyEqualTo("lenient-same.json"));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("source")
|
||||
void isStrictlyEqualToWhenResourceIsMatchingShouldPass(Resource expected) {
|
||||
assertThat(forJson(SOURCE)).isStrictlyEqualTo(expected);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("lenientSame")
|
||||
void isStrictlyEqualToWhenResourceIsNotMatchingShouldFail(Resource expected) {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isStrictlyEqualTo(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenStringIsMatchingShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotEqualTo(SOURCE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenNullActualShouldPass() {
|
||||
assertThat(forJson(null)).isNotEqualTo(SOURCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenStringIsNotMatchingShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isNotEqualTo(DIFFERENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToAsObjectWhenExpectedIsNotAStringShouldNotFail() {
|
||||
assertThat(forJson(SOURCE)).isNotEqualTo(SOURCE.getBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenStringIsMatchingAndLenientShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotEqualTo(LENIENT_SAME, JSONCompareMode.LENIENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenStringIsNotMatchingAndLenientShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isNotEqualTo(DIFFERENT, JSONCompareMode.LENIENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenResourcePathIsMatchingAndLenientShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(
|
||||
() -> assertThat(forJson(SOURCE)).isNotEqualTo("lenient-same.json", JSONCompareMode.LENIENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenResourcePathIsNotMatchingAndLenientShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isNotEqualTo("different.json", JSONCompareMode.LENIENT);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("lenientSame")
|
||||
void isNotEqualToWhenResourceIsMatchingAndLenientShouldFail(Resource expected) {
|
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> assertThat(forJson(SOURCE))
|
||||
.isNotEqualTo(expected, JSONCompareMode.LENIENT));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("different")
|
||||
void isNotEqualToWhenResourceIsNotMatchingAndLenientShouldPass(Resource expected) {
|
||||
assertThat(forJson(SOURCE)).isNotEqualTo(expected, JSONCompareMode.LENIENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenStringIsMatchingAndComparatorShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotEqualTo(LENIENT_SAME, comparator));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenStringIsNotMatchingAndComparatorShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isNotEqualTo(DIFFERENT, comparator);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenResourcePathIsMatchingAndComparatorShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotEqualTo("lenient-same.json", comparator));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenResourcePathIsNotMatchingAndComparatorShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isNotEqualTo("different.json", comparator);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("lenientSame")
|
||||
void isNotEqualToWhenResourceIsMatchingAndComparatorShouldFail(Resource expected) {
|
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(
|
||||
() -> assertThat(forJson(SOURCE)).isNotEqualTo(expected, comparator));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("different")
|
||||
void isNotEqualToWhenResourceIsNotMatchingAndComparatorShouldPass(Resource expected) {
|
||||
assertThat(forJson(SOURCE)).isNotEqualTo(expected, comparator);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenResourceIsMatchingAndComparatorShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotEqualTo(createResource(LENIENT_SAME), comparator));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenResourceIsNotMatchingAndComparatorShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isNotEqualTo(createResource(DIFFERENT), comparator);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotLenientlyEqualToWhenNullActualShouldPass() {
|
||||
assertThat(forJson(null)).isNotLenientlyEqualTo(SOURCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotLenientlyEqualToWhenStringIsNotMatchingShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isNotLenientlyEqualTo(DIFFERENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotLenientlyEqualToWhenResourcePathIsMatchingShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotLenientlyEqualTo("lenient-same.json"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotLenientlyEqualToWhenResourcePathIsNotMatchingShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isNotLenientlyEqualTo("different.json");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("lenientSame")
|
||||
void isNotLenientlyEqualToWhenResourceIsMatchingShouldFail(Resource expected) {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotLenientlyEqualTo(expected));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("different")
|
||||
void isNotLenientlyEqualToWhenResourceIsNotMatchingShouldPass(Resource expected) {
|
||||
assertThat(forJson(SOURCE)).isNotLenientlyEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotStrictlyEqualToWhenStringIsMatchingShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotStrictlyEqualTo(SOURCE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotStrictlyEqualToWhenStringIsNotMatchingShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isNotStrictlyEqualTo(LENIENT_SAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotStrictlyEqualToWhenResourcePathIsMatchingShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotStrictlyEqualTo("source.json"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotStrictlyEqualToWhenResourcePathIsNotMatchingShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isNotStrictlyEqualTo("lenient-same.json");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("source")
|
||||
void isNotStrictlyEqualToWhenResourceIsMatchingShouldFail(Resource expected) {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotStrictlyEqualTo(expected));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("lenientSame")
|
||||
void isNotStrictlyEqualToWhenResourceIsNotMatchingShouldPass(Resource expected) {
|
||||
assertThat(forJson(SOURCE)).isNotStrictlyEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withResourceLoadClassShouldAllowToLoadRelativeContent() {
|
||||
AbstractJsonContentAssert<?> jsonAssert = assertThat(forJson(NULLS)).withResourceLoadClass(String.class);
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> jsonAssert.isLenientlyEqualTo("nulls.json"))
|
||||
.withMessage("Unable to load JSON from class path resource [java/lang/nulls.json]");
|
||||
|
||||
assertThat(forJson(NULLS)).withResourceLoadClass(JsonContent.class).isLenientlyEqualTo("nulls.json");
|
||||
}
|
||||
|
||||
private AssertProvider<AbstractJsonContentAssert<?>> forJson(@Nullable String json) {
|
||||
return () -> new TestJsonContentAssert(json, null).withResourceLoadClass(getClass());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Consumer<AssertionError> hasFailedToMatchPath(String expression) {
|
||||
return error -> assertThat(error.getMessage()).containsSubsequence("Expecting:",
|
||||
"To match JSON path:", "\"" + expression + "\"");
|
||||
}
|
||||
|
||||
private Consumer<AssertionError> hasFailedToNotMatchPath(String expression) {
|
||||
return error -> assertThat(error.getMessage()).containsSubsequence("Expecting:",
|
||||
"Not to match JSON path:", "\"" + expression + "\"");
|
||||
}
|
||||
|
||||
private Path createFile(String content) {
|
||||
try {
|
||||
Path temp = Files.createTempFile("file", ".json");
|
||||
Files.writeString(temp, content);
|
||||
return temp;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream createInputStream(String content) {
|
||||
return new ByteArrayInputStream(content.getBytes());
|
||||
}
|
||||
|
||||
private Resource createResource(String content) {
|
||||
return new ByteArrayResource(content.getBytes());
|
||||
}
|
||||
|
||||
private static String loadJson(String path) {
|
||||
try {
|
||||
ClassPathResource resource = new ClassPathResource(path, AbstractJsonContentAssertTests.class);
|
||||
return new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private AssertProvider<AbstractJsonContentAssert<?>> forJson(@Nullable String json) {
|
||||
return () -> new TestJsonContentAssert(json, null);
|
||||
}
|
||||
|
||||
private static class TestJsonContentAssert extends AbstractJsonContentAssert<TestJsonContentAssert> {
|
||||
|
||||
public TestJsonContentAssert(@Nullable String json, @Nullable GenericHttpMessageConverter<Object> jsonMessageConverter) {
|
||||
super(json, jsonMessageConverter, TestJsonContentAssert.class);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,479 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.json;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.assertj.core.api.AssertProvider;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.api.TestInstance.Lifecycle;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.skyscreamer.jsonassert.JSONCompareMode;
|
||||
import org.skyscreamer.jsonassert.comparator.DefaultComparator;
|
||||
import org.skyscreamer.jsonassert.comparator.JSONComparator;
|
||||
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* Tests for {@link JsonContentAssert}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@TestInstance(Lifecycle.PER_CLASS)
|
||||
class JsonContentAssertTests {
|
||||
|
||||
private static final String SOURCE = loadJson("source.json");
|
||||
|
||||
private static final String LENIENT_SAME = loadJson("lenient-same.json");
|
||||
|
||||
private static final String DIFFERENT = loadJson("different.json");
|
||||
|
||||
private static final JSONComparator COMPARATOR = new DefaultComparator(JSONCompareMode.LENIENT);
|
||||
|
||||
@Test
|
||||
void isEqualToWhenStringIsMatchingShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isEqualTo(SOURCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenNullActualShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(() ->
|
||||
assertThat(forJson(null)).isEqualTo(SOURCE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenExpectedIsNotAStringShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isEqualTo(SOURCE.getBytes()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenExpectedIsNullShouldFail() {
|
||||
CharSequence actual = null;
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isEqualTo(actual, JSONCompareMode.LENIENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenStringIsMatchingAndLenientShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isEqualTo(LENIENT_SAME, JSONCompareMode.LENIENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenStringIsNotMatchingAndLenientShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isEqualTo(DIFFERENT, JSONCompareMode.LENIENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenResourcePathIsMatchingAndLenientShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isEqualTo("lenient-same.json", JSONCompareMode.LENIENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenResourcePathIsNotMatchingAndLenientShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isEqualTo("different.json", JSONCompareMode.LENIENT));
|
||||
}
|
||||
|
||||
Stream<Arguments> source() {
|
||||
return Stream.of(
|
||||
Arguments.of(new ClassPathResource("source.json", JsonContentAssertTests.class)),
|
||||
Arguments.of(new ByteArrayResource(SOURCE.getBytes())),
|
||||
Arguments.of(new FileSystemResource(createFile(SOURCE))),
|
||||
Arguments.of(new InputStreamResource(createInputStream(SOURCE))));
|
||||
}
|
||||
|
||||
Stream<Arguments> lenientSame() {
|
||||
return Stream.of(
|
||||
Arguments.of(new ClassPathResource("lenient-same.json", JsonContentAssertTests.class)),
|
||||
Arguments.of(new ByteArrayResource(LENIENT_SAME.getBytes())),
|
||||
Arguments.of(new FileSystemResource(createFile(LENIENT_SAME))),
|
||||
Arguments.of(new InputStreamResource(createInputStream(LENIENT_SAME))));
|
||||
}
|
||||
|
||||
Stream<Arguments> different() {
|
||||
return Stream.of(
|
||||
Arguments.of(new ClassPathResource("different.json", JsonContentAssertTests.class)),
|
||||
Arguments.of(new ByteArrayResource(DIFFERENT.getBytes())),
|
||||
Arguments.of(new FileSystemResource(createFile(DIFFERENT))),
|
||||
Arguments.of(new InputStreamResource(createInputStream(DIFFERENT))));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("lenientSame")
|
||||
void isEqualToWhenResourceIsMatchingAndLenientSameShouldPass(Resource expected) {
|
||||
assertThat(forJson(SOURCE)).isEqualTo(expected, JSONCompareMode.LENIENT);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("different")
|
||||
void isEqualToWhenResourceIsNotMatchingAndLenientShouldFail(Resource expected) {
|
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(
|
||||
() -> assertThat(forJson(SOURCE)).isEqualTo(expected, JSONCompareMode.LENIENT));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void isEqualToWhenStringIsMatchingAndComparatorShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isEqualTo(LENIENT_SAME, COMPARATOR);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenStringIsNotMatchingAndComparatorShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isEqualTo(DIFFERENT, COMPARATOR));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenResourcePathIsMatchingAndComparatorShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isEqualTo("lenient-same.json", COMPARATOR);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenResourcePathIsNotMatchingAndComparatorShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isEqualTo("different.json", COMPARATOR));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("lenientSame")
|
||||
void isEqualToWhenResourceIsMatchingAndComparatorShouldPass(Resource expected) {
|
||||
assertThat(forJson(SOURCE)).isEqualTo(expected, COMPARATOR);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("different")
|
||||
void isEqualToWhenResourceIsNotMatchingAndComparatorShouldFail(Resource expected) {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isEqualTo(expected, COMPARATOR));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isLenientlyEqualToWhenStringIsMatchingShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isLenientlyEqualTo(LENIENT_SAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isLenientlyEqualToWhenNullActualShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(null)).isLenientlyEqualTo(SOURCE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isLenientlyEqualToWhenStringIsNotMatchingShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isLenientlyEqualTo(DIFFERENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isLenientlyEqualToWhenExpectedDoesNotExistShouldFail() {
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isLenientlyEqualTo("does-not-exist.json"))
|
||||
.withMessage("Unable to load JSON from class path resource [org/springframework/test/json/does-not-exist.json]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void isLenientlyEqualToWhenResourcePathIsMatchingShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isLenientlyEqualTo("lenient-same.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void isLenientlyEqualToWhenResourcePathIsNotMatchingShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isLenientlyEqualTo("different.json"));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("lenientSame")
|
||||
void isLenientlyEqualToWhenResourceIsMatchingShouldPass(Resource expected) {
|
||||
assertThat(forJson(SOURCE)).isLenientlyEqualTo(expected);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("different")
|
||||
void isLenientlyEqualToWhenResourceIsNotMatchingShouldFail(Resource expected) {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isLenientlyEqualTo(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isStrictlyEqualToWhenStringIsMatchingShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isStrictlyEqualTo(SOURCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isStrictlyEqualToWhenStringIsNotMatchingShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isStrictlyEqualTo(LENIENT_SAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isStrictlyEqualToWhenResourcePathIsMatchingShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isStrictlyEqualTo("source.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void isStrictlyEqualToWhenResourcePathIsNotMatchingShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isStrictlyEqualTo("lenient-same.json"));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("source")
|
||||
void isStrictlyEqualToWhenResourceIsMatchingShouldPass(Resource expected) {
|
||||
assertThat(forJson(SOURCE)).isStrictlyEqualTo(expected);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("lenientSame")
|
||||
void isStrictlyEqualToWhenResourceIsNotMatchingShouldFail(Resource expected) {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isStrictlyEqualTo(expected));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenStringIsMatchingShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotEqualTo(SOURCE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenNullActualShouldPass() {
|
||||
assertThat(forJson(null)).isNotEqualTo(SOURCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenStringIsNotMatchingShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isNotEqualTo(DIFFERENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToAsObjectWhenExpectedIsNotAStringShouldNotFail() {
|
||||
assertThat(forJson(SOURCE)).isNotEqualTo(SOURCE.getBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenStringIsMatchingAndLenientShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotEqualTo(LENIENT_SAME, JSONCompareMode.LENIENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenStringIsNotMatchingAndLenientShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isNotEqualTo(DIFFERENT, JSONCompareMode.LENIENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenResourcePathIsMatchingAndLenientShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(
|
||||
() -> assertThat(forJson(SOURCE)).isNotEqualTo("lenient-same.json", JSONCompareMode.LENIENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenResourcePathIsNotMatchingAndLenientShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isNotEqualTo("different.json", JSONCompareMode.LENIENT);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("lenientSame")
|
||||
void isNotEqualToWhenResourceIsMatchingAndLenientShouldFail(Resource expected) {
|
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> assertThat(forJson(SOURCE))
|
||||
.isNotEqualTo(expected, JSONCompareMode.LENIENT));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("different")
|
||||
void isNotEqualToWhenResourceIsNotMatchingAndLenientShouldPass(Resource expected) {
|
||||
assertThat(forJson(SOURCE)).isNotEqualTo(expected, JSONCompareMode.LENIENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenStringIsMatchingAndComparatorShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotEqualTo(LENIENT_SAME, COMPARATOR));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenStringIsNotMatchingAndComparatorShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isNotEqualTo(DIFFERENT, COMPARATOR);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenResourcePathIsMatchingAndComparatorShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotEqualTo("lenient-same.json", COMPARATOR));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenResourcePathIsNotMatchingAndComparatorShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isNotEqualTo("different.json", COMPARATOR);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("lenientSame")
|
||||
void isNotEqualToWhenResourceIsMatchingAndComparatorShouldFail(Resource expected) {
|
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(
|
||||
() -> assertThat(forJson(SOURCE)).isNotEqualTo(expected, COMPARATOR));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("different")
|
||||
void isNotEqualToWhenResourceIsNotMatchingAndComparatorShouldPass(Resource expected) {
|
||||
assertThat(forJson(SOURCE)).isNotEqualTo(expected, COMPARATOR);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenResourceIsMatchingAndComparatorShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotEqualTo(createResource(LENIENT_SAME), COMPARATOR));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEqualToWhenResourceIsNotMatchingAndComparatorShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isNotEqualTo(createResource(DIFFERENT), COMPARATOR);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotLenientlyEqualToWhenNullActualShouldPass() {
|
||||
assertThat(forJson(null)).isNotLenientlyEqualTo(SOURCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotLenientlyEqualToWhenStringIsNotMatchingShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isNotLenientlyEqualTo(DIFFERENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotLenientlyEqualToWhenResourcePathIsMatchingShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotLenientlyEqualTo("lenient-same.json"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotLenientlyEqualToWhenResourcePathIsNotMatchingShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isNotLenientlyEqualTo("different.json");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("lenientSame")
|
||||
void isNotLenientlyEqualToWhenResourceIsMatchingShouldFail(Resource expected) {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotLenientlyEqualTo(expected));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("different")
|
||||
void isNotLenientlyEqualToWhenResourceIsNotMatchingShouldPass(Resource expected) {
|
||||
assertThat(forJson(SOURCE)).isNotLenientlyEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotStrictlyEqualToWhenStringIsMatchingShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotStrictlyEqualTo(SOURCE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotStrictlyEqualToWhenStringIsNotMatchingShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isNotStrictlyEqualTo(LENIENT_SAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotStrictlyEqualToWhenResourcePathIsMatchingShouldFail() {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotStrictlyEqualTo("source.json"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotStrictlyEqualToWhenResourcePathIsNotMatchingShouldPass() {
|
||||
assertThat(forJson(SOURCE)).isNotStrictlyEqualTo("lenient-same.json");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("source")
|
||||
void isNotStrictlyEqualToWhenResourceIsMatchingShouldFail(Resource expected) {
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotStrictlyEqualTo(expected));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("lenientSame")
|
||||
void isNotStrictlyEqualToWhenResourceIsNotMatchingShouldPass(Resource expected) {
|
||||
assertThat(forJson(SOURCE)).isNotStrictlyEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNullWhenActualIsNullShouldPass() {
|
||||
assertThat(forJson(null)).isNull();
|
||||
}
|
||||
|
||||
private Path createFile(String content) {
|
||||
try {
|
||||
Path temp = Files.createTempFile("file", ".json");
|
||||
Files.writeString(temp, content);
|
||||
return temp;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream createInputStream(String content) {
|
||||
return new ByteArrayInputStream(content.getBytes());
|
||||
}
|
||||
|
||||
private Resource createResource(String content) {
|
||||
return new ByteArrayResource(content.getBytes());
|
||||
}
|
||||
|
||||
private static String loadJson(String path) {
|
||||
try {
|
||||
ClassPathResource resource = new ClassPathResource(path, JsonContentAssertTests.class);
|
||||
return new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private AssertProvider<JsonContentAssert> forJson(@Nullable String json) {
|
||||
return () -> new JsonContentAssert(json, JsonContentAssertTests.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,323 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.json;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.assertj.core.api.AssertProvider;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.http.converter.GenericHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
|
||||
/**
|
||||
* Tests for {@link JsonPathAssert}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class JsonPathAssertTests {
|
||||
|
||||
private static final String TYPES = loadJson("types.json");
|
||||
|
||||
private static final String SIMPSONS = loadJson("simpsons.json");
|
||||
|
||||
private static final String NULLS = loadJson("nulls.json");
|
||||
|
||||
private static final MappingJackson2HttpMessageConverter jsonHttpMessageConverter =
|
||||
new MappingJackson2HttpMessageConverter(new ObjectMapper());
|
||||
|
||||
|
||||
@Nested
|
||||
class HasPathTests {
|
||||
|
||||
@Test
|
||||
void hasPathForPresentAndNotNull() {
|
||||
assertThat(forJson(NULLS)).hasPath("$.valuename");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasPathForPresentAndNull() {
|
||||
assertThat(forJson(NULLS)).hasPath("$.nullname");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasPathForOperatorMatching() {
|
||||
assertThat(forJson(SIMPSONS)).
|
||||
hasPath("$.familyMembers[?(@.name == 'Homer')]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasPathForOperatorNotMatching() {
|
||||
assertThat(forJson(SIMPSONS)).
|
||||
hasPath("$.familyMembers[?(@.name == 'Dilbert')]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasPathForNotPresent() {
|
||||
String expression = "$.missing";
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(NULLS)).hasPath(expression))
|
||||
.satisfies(hasFailedToMatchPath("$.missing"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasPathSatisfying() {
|
||||
assertThat(forJson(TYPES)).hasPathSatisfying("$.str", value -> assertThat(value).isEqualTo("foo"))
|
||||
.hasPathSatisfying("$.num", value -> assertThat(value).isEqualTo(5));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasPathSatisfyingForPathNotPresent() {
|
||||
String expression = "missing";
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(NULLS)).hasPathSatisfying(expression, value -> {}))
|
||||
.satisfies(hasFailedToMatchPath(expression));
|
||||
}
|
||||
|
||||
@Test
|
||||
void doesNotHavePathForMissing() {
|
||||
assertThat(forJson(NULLS)).doesNotHavePath("$.missing");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void doesNotHavePathForPresent() {
|
||||
String expression = "$.valuename";
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(NULLS)).doesNotHavePath(expression))
|
||||
.satisfies(hasFailedToNotMatchPath(expression));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class ExtractingPathTests {
|
||||
|
||||
@Test
|
||||
void isNullWithNullPathValue() {
|
||||
assertThat(forJson(NULLS)).extractingPath("$.nullname").isNull();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "$.str", "$.emptyString", "$.num", "$.bool", "$.arr",
|
||||
"$.emptyArray", "$.colorMap", "$.emptyMap" })
|
||||
void isNotNullWithValue(String path) {
|
||||
assertThat(forJson(TYPES)).extractingPath(path).isNotNull();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void isEqualToOnRawValue(String path, Object expected) {
|
||||
assertThat(forJson(TYPES)).extractingPath(path).isEqualTo(expected);
|
||||
}
|
||||
|
||||
static Stream<Arguments> isEqualToOnRawValue() {
|
||||
return Stream.of(
|
||||
Arguments.of("$.str", "foo"),
|
||||
Arguments.of("$.num", 5),
|
||||
Arguments.of("$.bool", true),
|
||||
Arguments.of("$.arr", List.of(42)),
|
||||
Arguments.of("$.colorMap", Map.of("red", "rojo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void asStringWithActualValue() {
|
||||
assertThat(forJson(TYPES)).extractingPath("@.str").asString().startsWith("f").endsWith("o");
|
||||
}
|
||||
|
||||
@Test
|
||||
void asStringIsEmpty() {
|
||||
assertThat(forJson(TYPES)).extractingPath("@.emptyString").asString().isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void asNumberWithActualValue() {
|
||||
assertThat(forJson(TYPES)).extractingPath("@.num").asNumber().isEqualTo(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
void asBooleanWithActualValue() {
|
||||
assertThat(forJson(TYPES)).extractingPath("@.bool").asBoolean().isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void asArrayWithActualValue() {
|
||||
assertThat(forJson(TYPES)).extractingPath("@.arr").asArray().containsOnly(42);
|
||||
}
|
||||
|
||||
@Test
|
||||
void asArrayIsEmpty() {
|
||||
assertThat(forJson(TYPES)).extractingPath("@.emptyArray").asArray().isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void asArrayWithFilterPredicatesMatching() {
|
||||
assertThat(forJson(SIMPSONS))
|
||||
.extractingPath("$.familyMembers[?(@.name == 'Bart')]").asArray().hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void asArrayWithFilterPredicatesNotMatching() {
|
||||
assertThat(forJson(SIMPSONS)).
|
||||
extractingPath("$.familyMembers[?(@.name == 'Dilbert')]").asArray().isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void asMapWithActualValue() {
|
||||
assertThat(forJson(TYPES)).extractingPath("@.colorMap").asMap().containsOnly(entry("red", "rojo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void asMapIsEmpty() {
|
||||
assertThat(forJson(TYPES)).extractingPath("@.emptyMap").asMap().isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void convertToWithoutHttpMessageConverterShouldFail() {
|
||||
JsonPathValueAssert path = assertThat(forJson(SIMPSONS)).extractingPath("$.familyMembers[0]");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> path.convertTo(Member.class))
|
||||
.withMessage("No JSON message converter available to convert {name=Homer}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void convertToTargetType() {
|
||||
assertThat(forJson(SIMPSONS, jsonHttpMessageConverter))
|
||||
.extractingPath("$.familyMembers[0]").convertTo(Member.class)
|
||||
.satisfies(member -> assertThat(member.name).isEqualTo("Homer"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void convertToIncompatibleTargetTypeShouldFail() {
|
||||
JsonPathValueAssert path = assertThat(forJson(SIMPSONS, jsonHttpMessageConverter))
|
||||
.extractingPath("$.familyMembers[0]");
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> path.convertTo(Customer.class))
|
||||
.withMessageContainingAll("Expected value at JSON path \"$.familyMembers[0]\":",
|
||||
Customer.class.getName(), "name");
|
||||
}
|
||||
|
||||
@Test
|
||||
void convertArrayToParameterizedType() {
|
||||
assertThat(forJson(SIMPSONS, jsonHttpMessageConverter))
|
||||
.extractingPath("$.familyMembers")
|
||||
.convertTo(new ParameterizedTypeReference<List<Member>>() {})
|
||||
.satisfies(family -> assertThat(family).hasSize(5).element(0).isEqualTo(new Member("Homer")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEmptyWithPathHavingNullValue() {
|
||||
assertThat(forJson(NULLS)).extractingPath("nullname").isEmpty();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "$.emptyString", "$.emptyArray", "$.emptyMap" })
|
||||
void isEmptyWithEmptyValue(String path) {
|
||||
assertThat(forJson(TYPES)).extractingPath(path).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEmptyForPathWithFilterMatching() {
|
||||
String expression = "$.familyMembers[?(@.name == 'Bart')]";
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SIMPSONS)).extractingPath(expression).isEmpty())
|
||||
.withMessageContainingAll("Expected value at JSON path \"" + expression + "\"",
|
||||
"[{\"name\":\"Bart\"}]", "To be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEmptyForPathWithFilterNotMatching() {
|
||||
assertThat(forJson(SIMPSONS)).extractingPath("$.familyMembers[?(@.name == 'Dilbert')]").isEmpty();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "$.str", "$.num", "$.bool", "$.arr", "$.colorMap" })
|
||||
void isNotEmptyWithNonNullValue(String path) {
|
||||
assertThat(forJson(TYPES)).extractingPath(path).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEmptyForPathWithFilterMatching() {
|
||||
assertThat(forJson(SIMPSONS)).extractingPath("$.familyMembers[?(@.name == 'Bart')]").isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEmptyForPathWithFilterNotMatching() {
|
||||
String expression = "$.familyMembers[?(@.name == 'Dilbert')]";
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(forJson(SIMPSONS)).extractingPath(expression).isNotEmpty())
|
||||
.withMessageContainingAll("Expected value at JSON path \"" + expression + "\"",
|
||||
"To not be empty");
|
||||
}
|
||||
|
||||
|
||||
private record Member(String name) {}
|
||||
|
||||
private record Customer(long id, String username) {}
|
||||
|
||||
}
|
||||
|
||||
private Consumer<AssertionError> hasFailedToMatchPath(String expression) {
|
||||
return error -> assertThat(error.getMessage()).containsSubsequence("Expecting:",
|
||||
"To match JSON path:", "\"" + expression + "\"");
|
||||
}
|
||||
|
||||
private Consumer<AssertionError> hasFailedToNotMatchPath(String expression) {
|
||||
return error -> assertThat(error.getMessage()).containsSubsequence("Expecting:",
|
||||
"Not to match JSON path:", "\"" + expression + "\"");
|
||||
}
|
||||
|
||||
|
||||
private static String loadJson(String path) {
|
||||
try {
|
||||
ClassPathResource resource = new ClassPathResource(path, JsonPathAssertTests.class);
|
||||
return new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private AssertProvider<JsonPathAssert> forJson(String json) {
|
||||
return forJson(json, null);
|
||||
}
|
||||
|
||||
private AssertProvider<JsonPathAssert> forJson(String json,
|
||||
@Nullable GenericHttpMessageConverter<Object> jsonHttpMessageConverter) {
|
||||
return () -> new JsonPathAssert(json, jsonHttpMessageConverter);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ import org.junit.jupiter.api.Nested;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
|
@ -37,13 +38,30 @@ class AbstractHttpServletResponseAssertTests {
|
|||
@Nested
|
||||
class HeadersTests {
|
||||
|
||||
@Test
|
||||
void containsHeader() {
|
||||
MockHttpServletResponse response = createResponse(Map.of("n1", "v1", "n2", "v2", "n3", "v3"));
|
||||
assertThat(response).containsHeader("n1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void doesNotContainHeader() {
|
||||
MockHttpServletResponse response = createResponse(Map.of("n1", "v1", "n2", "v2", "n3", "v3"));
|
||||
assertThat(response).doesNotContainHeader("n4");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasHeader() {
|
||||
MockHttpServletResponse response = createResponse(Map.of("n1", "v1", "n2", "v2", "n3", "v3"));
|
||||
assertThat(response).hasHeader("n1", "v1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void headersAreMatching() {
|
||||
MockHttpServletResponse response = createResponse(Map.of("n1", "v1", "n2", "v2", "n3", "v3"));
|
||||
assertThat(response).headers().containsHeaders("n1", "n2", "n3");
|
||||
}
|
||||
|
||||
|
||||
private MockHttpServletResponse createResponse(Map<String, String> headers) {
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
headers.forEach(response::addHeader);
|
||||
|
|
@ -51,6 +69,45 @@ class AbstractHttpServletResponseAssertTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ContentTypeTests {
|
||||
|
||||
@Test
|
||||
void contentType() {
|
||||
MockHttpServletResponse response = createResponse("text/plain");
|
||||
assertThat(response).hasContentType(MediaType.TEXT_PLAIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void contentTypeAndRepresentation() {
|
||||
MockHttpServletResponse response = createResponse("text/plain");
|
||||
assertThat(response).hasContentType("text/plain");
|
||||
}
|
||||
|
||||
@Test
|
||||
void contentTypeCompatibleWith() {
|
||||
MockHttpServletResponse response = createResponse("application/json;charset=UTF-8");
|
||||
assertThat(response).hasContentTypeCompatibleWith(MediaType.APPLICATION_JSON);
|
||||
}
|
||||
|
||||
@Test
|
||||
void contentTypeCompatibleWithAndStringRepresentation() {
|
||||
MockHttpServletResponse response = createResponse("text/plain");
|
||||
assertThat(response).hasContentTypeCompatibleWith("text/*");
|
||||
}
|
||||
|
||||
@Test
|
||||
void contentTypeCanBeAsserted() {
|
||||
MockHttpServletResponse response = createResponse("text/plain");
|
||||
assertThat(response).contentType().isInstanceOf(MediaType.class).isCompatibleWith("text/*").isNotNull();
|
||||
}
|
||||
|
||||
private MockHttpServletResponse createResponse(String contentType) {
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
response.setContentType(contentType);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class StatusTests {
|
||||
|
|
|
|||
|
|
@ -16,15 +16,17 @@
|
|||
|
||||
package org.springframework.test.web.servlet.assertj;
|
||||
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.assertj.core.api.AssertProvider;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.test.json.JsonContent;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
|
|
@ -34,12 +36,50 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
|||
*/
|
||||
public class AbstractMockHttpServletResponseAssertTests {
|
||||
|
||||
@Test
|
||||
void bodyText() {
|
||||
MockHttpServletResponse response = createResponse("OK");
|
||||
assertThat(fromResponse(response)).bodyText().isEqualTo("OK");
|
||||
}
|
||||
|
||||
@Test
|
||||
void bodyJsonWithJsonPath() {
|
||||
MockHttpServletResponse response = createResponse("{\"albumById\": {\"name\": \"Greatest hits\"}}");
|
||||
assertThat(fromResponse(response)).bodyJson()
|
||||
.extractingPath("$.albumById.name").isEqualTo("Greatest hits");
|
||||
}
|
||||
|
||||
@Test
|
||||
void bodyJsonCanLoadResourceRelativeToClass() {
|
||||
MockHttpServletResponse response = createResponse("{ \"name\" : \"Spring\", \"age\" : 123 }");
|
||||
// See org/springframework/test/json/example.json
|
||||
assertThat(fromResponse(response)).bodyJson().withResourceLoadClass(JsonContent.class)
|
||||
.isLenientlyEqualTo("example.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void bodyWithByteArray() throws UnsupportedEncodingException {
|
||||
byte[] bytes = "OK".getBytes(StandardCharsets.UTF_8);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
response.getWriter().write("OK");
|
||||
response.setContentType(StandardCharsets.UTF_8.name());
|
||||
assertThat(fromResponse(response)).body().isEqualTo(bytes);
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasBodyTextEqualTo() throws UnsupportedEncodingException {
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
response.getWriter().write("OK");
|
||||
response.setContentType(StandardCharsets.UTF_8.name());
|
||||
assertThat(fromResponse(response)).hasBodyTextEqualTo("OK");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasForwardedUrl() {
|
||||
String forwardedUrl = "https://example.com/42";
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
response.setForwardedUrl(forwardedUrl);
|
||||
assertThat(response).hasForwardedUrl(forwardedUrl);
|
||||
assertThat(fromResponse(response)).hasForwardedUrl(forwardedUrl);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -48,7 +88,7 @@ public class AbstractMockHttpServletResponseAssertTests {
|
|||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
response.setForwardedUrl(forwardedUrl);
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(response).hasForwardedUrl("another"))
|
||||
.isThrownBy(() -> assertThat(fromResponse(response)).hasForwardedUrl("another"))
|
||||
.withMessageContainingAll("Forwarded URL", forwardedUrl, "another");
|
||||
}
|
||||
|
||||
|
|
@ -57,7 +97,7 @@ public class AbstractMockHttpServletResponseAssertTests {
|
|||
String redirectedUrl = "https://example.com/42";
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
response.addHeader(HttpHeaders.LOCATION, redirectedUrl);
|
||||
assertThat(response).hasRedirectedUrl(redirectedUrl);
|
||||
assertThat(fromResponse(response)).hasRedirectedUrl(redirectedUrl);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -66,29 +106,25 @@ public class AbstractMockHttpServletResponseAssertTests {
|
|||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
response.addHeader(HttpHeaders.LOCATION, redirectedUrl);
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(response).hasRedirectedUrl("another"))
|
||||
.isThrownBy(() -> assertThat(fromResponse(response)).hasRedirectedUrl("another"))
|
||||
.withMessageContainingAll("Redirected URL", redirectedUrl, "another");
|
||||
}
|
||||
|
||||
@Test
|
||||
void bodyHasContent() throws UnsupportedEncodingException {
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
response.getWriter().write("OK");
|
||||
assertThat(response).body().asString().isEqualTo("OK");
|
||||
|
||||
private MockHttpServletResponse createResponse(String body) {
|
||||
try {
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
response.setContentType(StandardCharsets.UTF_8.name());
|
||||
response.getWriter().write(body);
|
||||
return response;
|
||||
}
|
||||
catch (UnsupportedEncodingException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void bodyHasContentWithResponseCharacterEncoding() throws UnsupportedEncodingException {
|
||||
byte[] bytes = "OK".getBytes(StandardCharsets.UTF_8);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
response.getWriter().write("OK");
|
||||
response.setContentType(StandardCharsets.UTF_8.name());
|
||||
assertThat(response).body().isEqualTo(bytes);
|
||||
}
|
||||
|
||||
|
||||
private static ResponseAssert assertThat(MockHttpServletResponse response) {
|
||||
return new ResponseAssert(response);
|
||||
private static AssertProvider<ResponseAssert> fromResponse(MockHttpServletResponse response) {
|
||||
return () -> new ResponseAssert(response);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -30,76 +30,47 @@ import static org.mockito.Mockito.mock;
|
|||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link DefaultAssertableMvcResult}.
|
||||
* Tests for {@link DefaultMvcTestResult}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class DefaultAssertableMvcResultTests {
|
||||
class DefaultMvcTestResultTests {
|
||||
|
||||
@Test
|
||||
void createWithMvcResultDelegatesToIt() {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MvcResult mvcResult = mock(MvcResult.class);
|
||||
given(mvcResult.getRequest()).willReturn(request);
|
||||
DefaultAssertableMvcResult result = new DefaultAssertableMvcResult(mvcResult, null, null);
|
||||
DefaultMvcTestResult result = new DefaultMvcTestResult(mvcResult, null, null);
|
||||
assertThat(result.getRequest()).isSameAs(request);
|
||||
verify(mvcResult).getRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithExceptionDoesNotAllowAccessToRequest() {
|
||||
assertRequestHasFailed(DefaultAssertableMvcResult::getRequest);
|
||||
assertRequestHasFailed(DefaultMvcTestResult::getRequest);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithExceptionDoesNotAllowAccessToResponse() {
|
||||
assertRequestHasFailed(DefaultAssertableMvcResult::getResponse);
|
||||
assertRequestHasFailed(DefaultMvcTestResult::getResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithExceptionDoesNotAllowAccessToHandler() {
|
||||
assertRequestHasFailed(DefaultAssertableMvcResult::getHandler);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithExceptionDoesNotAllowAccessToInterceptors() {
|
||||
assertRequestHasFailed(DefaultAssertableMvcResult::getInterceptors);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithExceptionDoesNotAllowAccessToModelAndView() {
|
||||
assertRequestHasFailed(DefaultAssertableMvcResult::getModelAndView);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithExceptionDoesNotAllowAccessToResolvedException() {
|
||||
assertRequestHasFailed(DefaultAssertableMvcResult::getResolvedException);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithExceptionDoesNotAllowAccessToFlashMap() {
|
||||
assertRequestHasFailed(DefaultAssertableMvcResult::getFlashMap);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithExceptionDoesNotAllowAccessToAsyncResult() {
|
||||
assertRequestHasFailed(DefaultAssertableMvcResult::getAsyncResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithExceptionDoesNotAllowAccessToAsyncResultWithTimeToWait() {
|
||||
assertRequestHasFailed(result -> result.getAsyncResult(1000));
|
||||
assertRequestHasFailed(DefaultMvcTestResult::getResolvedException);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithExceptionReturnsException() {
|
||||
IllegalStateException exception = new IllegalStateException("Expected");
|
||||
DefaultAssertableMvcResult result = new DefaultAssertableMvcResult(null, exception, null);
|
||||
DefaultMvcTestResult result = new DefaultMvcTestResult(null, exception, null);
|
||||
assertThat(result.getUnresolvedException()).isSameAs(exception);
|
||||
}
|
||||
|
||||
private void assertRequestHasFailed(Consumer<DefaultAssertableMvcResult> action) {
|
||||
DefaultAssertableMvcResult result = new DefaultAssertableMvcResult(null, new IllegalStateException("Expected"), null);
|
||||
private void assertRequestHasFailed(Consumer<DefaultMvcTestResult> action) {
|
||||
DefaultMvcTestResult result = new DefaultMvcTestResult(null, new IllegalStateException("Expected"), null);
|
||||
assertThatIllegalStateException().isThrownBy(() -> action.accept(result))
|
||||
.withMessageContaining("Request has failed with unresolved exception");
|
||||
}
|
||||
|
|
@ -43,7 +43,6 @@ import org.springframework.stereotype.Controller;
|
|||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.Person;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.Errors;
|
||||
|
|
@ -69,19 +68,19 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
|
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link AssertableMockMvc}.
|
||||
* Integration tests for {@link MockMvcTester}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@WebAppConfiguration
|
||||
public class AssertableMockMvcIntegrationTests {
|
||||
public class MockMvcTesterIntegrationTests {
|
||||
|
||||
private final AssertableMockMvc mockMvc;
|
||||
private final MockMvcTester mockMvc;
|
||||
|
||||
AssertableMockMvcIntegrationTests(WebApplicationContext wac) {
|
||||
this.mockMvc = AssertableMockMvc.from(wac);
|
||||
MockMvcTesterIntegrationTests(WebApplicationContext wac) {
|
||||
this.mockMvc = MockMvcTester.from(wac);
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
|
@ -126,8 +125,8 @@ public class AssertableMockMvcIntegrationTests {
|
|||
assertThat(performWithCookie(cookie, get("/greet"))).cookies().hasValue("test", "value");
|
||||
}
|
||||
|
||||
private AssertableMvcResult performWithCookie(Cookie cookie, MockHttpServletRequestBuilder request) {
|
||||
AssertableMockMvc mockMvc = AssertableMockMvc.of(List.of(new TestController()), builder -> builder.addInterceptors(
|
||||
private MvcTestResult performWithCookie(Cookie cookie, MockHttpServletRequestBuilder request) {
|
||||
MockMvcTester mockMvc = MockMvcTester.of(List.of(new TestController()), builder -> builder.addInterceptors(
|
||||
new HandlerInterceptor() {
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
|
|
@ -139,16 +138,6 @@ public class AssertableMockMvcIntegrationTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ContentTypeTests {
|
||||
|
||||
@Test
|
||||
void contentType() {
|
||||
assertThat(perform(get("/greet"))).contentType().isCompatibleWith("text/plain");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class StatusTests {
|
||||
|
||||
|
|
@ -169,8 +158,8 @@ public class AssertableMockMvcIntegrationTests {
|
|||
|
||||
@Test
|
||||
void shouldAssertHeader() {
|
||||
assertThat(perform(get("/greet"))).headers()
|
||||
.hasValue("Content-Type", "text/plain;charset=ISO-8859-1");
|
||||
assertThat(perform(get("/greet")))
|
||||
.hasHeader("Content-Type", "text/plain;charset=ISO-8859-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -250,19 +239,19 @@ public class AssertableMockMvcIntegrationTests {
|
|||
|
||||
@Test
|
||||
void jsonPathContent() {
|
||||
assertThat(perform(get("/message"))).body().jsonPath()
|
||||
assertThat(perform(get("/message"))).bodyJson()
|
||||
.extractingPath("$.message").asString().isEqualTo("hello");
|
||||
}
|
||||
|
||||
@Test
|
||||
void jsonContentCanLoadResourceFromClasspath() {
|
||||
assertThat(perform(get("/message"))).body().json().isLenientlyEqualTo(
|
||||
new ClassPathResource("message.json", AssertableMockMvcIntegrationTests.class));
|
||||
assertThat(perform(get("/message"))).bodyJson().isLenientlyEqualTo(
|
||||
new ClassPathResource("message.json", MockMvcTesterIntegrationTests.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void jsonContentUsingResourceLoaderClass() {
|
||||
assertThat(perform(get("/message"))).body().json(AssertableMockMvcIntegrationTests.class)
|
||||
assertThat(perform(get("/message"))).bodyJson().withResourceLoadClass(MockMvcTesterIntegrationTests.class)
|
||||
.isLenientlyEqualTo("message.json");
|
||||
}
|
||||
|
||||
|
|
@ -416,8 +405,8 @@ public class AssertableMockMvcIntegrationTests {
|
|||
}
|
||||
|
||||
|
||||
private void testAssertionFailureWithUnresolvableException(Consumer<AssertableMvcResult> assertions) {
|
||||
AssertableMvcResult result = perform(get("/error/1"));
|
||||
private void testAssertionFailureWithUnresolvableException(Consumer<MvcTestResult> assertions) {
|
||||
MvcTestResult result = perform(get("/error/1"));
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertions.accept(result))
|
||||
.withMessageContainingAll("Request has failed unexpectedly:",
|
||||
|
|
@ -441,7 +430,7 @@ public class AssertableMockMvcIntegrationTests {
|
|||
@Test
|
||||
void satisfiesAllowsAdditionalAssertions() {
|
||||
assertThat(this.mockMvc.perform(get("/greet"))).satisfies(result -> {
|
||||
assertThat(result).isInstanceOf(MvcResult.class);
|
||||
assertThat(result).isInstanceOf(MvcTestResult.class);
|
||||
assertThat(result).hasStatusOk();
|
||||
});
|
||||
}
|
||||
|
|
@ -467,7 +456,7 @@ public class AssertableMockMvcIntegrationTests {
|
|||
}
|
||||
|
||||
|
||||
private AssertableMvcResult perform(MockHttpServletRequestBuilder builder) {
|
||||
private MvcTestResult perform(MockHttpServletRequestBuilder builder) {
|
||||
return this.mockMvc.perform(builder);
|
||||
}
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ import org.springframework.http.HttpStatus;
|
|||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.test.json.JsonPathAssert;
|
||||
import org.springframework.test.json.AbstractJsonContentAssert;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
|
@ -48,11 +48,11 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
|
|||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
|
||||
/**
|
||||
* Tests for {@link AssertableMockMvc}.
|
||||
* Tests for {@link MockMvcTester}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class AssertableMockMvcTests {
|
||||
class MockMvcTesterTests {
|
||||
|
||||
private static final MappingJackson2HttpMessageConverter jsonHttpMessageConverter =
|
||||
new MappingJackson2HttpMessageConverter(new ObjectMapper());
|
||||
|
|
@ -60,56 +60,56 @@ class AssertableMockMvcTests {
|
|||
|
||||
@Test
|
||||
void createShouldRejectNullMockMvc() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> AssertableMockMvc.create(null));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> MockMvcTester.create(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithExistingWebApplicationContext() {
|
||||
try (GenericWebApplicationContext wac = create(WebConfiguration.class)) {
|
||||
AssertableMockMvc mockMvc = AssertableMockMvc.from(wac);
|
||||
assertThat(mockMvc.perform(post("/increase"))).body().isEqualTo("counter 41");
|
||||
assertThat(mockMvc.perform(post("/increase"))).body().isEqualTo("counter 42");
|
||||
MockMvcTester mockMvc = MockMvcTester.from(wac);
|
||||
assertThat(mockMvc.perform(post("/increase"))).hasBodyTextEqualTo("counter 41");
|
||||
assertThat(mockMvc.perform(post("/increase"))).hasBodyTextEqualTo("counter 42");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithControllerClassShouldInstantiateControllers() {
|
||||
AssertableMockMvc mockMvc = AssertableMockMvc.of(HelloController.class, CounterController.class);
|
||||
assertThat(mockMvc.perform(get("/hello"))).body().isEqualTo("Hello World");
|
||||
assertThat(mockMvc.perform(post("/increase"))).body().isEqualTo("counter 1");
|
||||
assertThat(mockMvc.perform(post("/increase"))).body().isEqualTo("counter 2");
|
||||
MockMvcTester mockMvc = MockMvcTester.of(HelloController.class, CounterController.class);
|
||||
assertThat(mockMvc.perform(get("/hello"))).hasBodyTextEqualTo("Hello World");
|
||||
assertThat(mockMvc.perform(post("/increase"))).hasBodyTextEqualTo("counter 1");
|
||||
assertThat(mockMvc.perform(post("/increase"))).hasBodyTextEqualTo("counter 2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithControllersShouldUseThemAsIs() {
|
||||
AssertableMockMvc mockMvc = AssertableMockMvc.of(new HelloController(),
|
||||
MockMvcTester mockMvc = MockMvcTester.of(new HelloController(),
|
||||
new CounterController(new AtomicInteger(41)));
|
||||
assertThat(mockMvc.perform(get("/hello"))).body().isEqualTo("Hello World");
|
||||
assertThat(mockMvc.perform(post("/increase"))).body().isEqualTo("counter 42");
|
||||
assertThat(mockMvc.perform(post("/increase"))).body().isEqualTo("counter 43");
|
||||
assertThat(mockMvc.perform(get("/hello"))).hasBodyTextEqualTo("Hello World");
|
||||
assertThat(mockMvc.perform(post("/increase"))).hasBodyTextEqualTo("counter 42");
|
||||
assertThat(mockMvc.perform(post("/increase"))).hasBodyTextEqualTo("counter 43");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithControllerAndCustomizations() {
|
||||
AssertableMockMvc mockMvc = AssertableMockMvc.of(List.of(new HelloController()), builder ->
|
||||
MockMvcTester mockMvc = MockMvcTester.of(List.of(new HelloController()), builder ->
|
||||
builder.defaultRequest(get("/hello").accept(MediaType.APPLICATION_JSON)).build());
|
||||
assertThat(mockMvc.perform(get("/hello"))).hasStatus(HttpStatus.NOT_ACCEPTABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithControllersHasNoHttpMessageConverter() {
|
||||
AssertableMockMvc mockMvc = AssertableMockMvc.of(new HelloController());
|
||||
JsonPathAssert jsonPathAssert = assertThat(mockMvc.perform(get("/json"))).hasStatusOk().body().jsonPath();
|
||||
MockMvcTester mockMvc = MockMvcTester.of(new HelloController());
|
||||
AbstractJsonContentAssert<?> jsonContentAssert = assertThat(mockMvc.perform(get("/json"))).hasStatusOk().bodyJson();
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> jsonPathAssert.extractingPath("$").convertTo(Message.class))
|
||||
.isThrownBy(() -> jsonContentAssert.extractingPath("$").convertTo(Message.class))
|
||||
.withMessageContaining("No JSON message converter available");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithControllerCanConfigureHttpMessageConverters() {
|
||||
AssertableMockMvc mockMvc = AssertableMockMvc.of(HelloController.class)
|
||||
MockMvcTester mockMvc = MockMvcTester.of(HelloController.class)
|
||||
.withHttpMessageConverters(List.of(jsonHttpMessageConverter));
|
||||
assertThat(mockMvc.perform(get("/json"))).hasStatusOk().body().jsonPath()
|
||||
assertThat(mockMvc.perform(get("/json"))).hasStatusOk().bodyJson()
|
||||
.extractingPath("$").convertTo(Message.class).satisfies(message -> {
|
||||
assertThat(message.message()).isEqualTo("Hello World");
|
||||
assertThat(message.counter()).isEqualTo(42);
|
||||
|
|
@ -120,9 +120,9 @@ class AssertableMockMvcTests {
|
|||
@SuppressWarnings("unchecked")
|
||||
void withHttpMessageConverterDetectsJsonConverter() {
|
||||
MappingJackson2HttpMessageConverter converter = spy(jsonHttpMessageConverter);
|
||||
AssertableMockMvc mockMvc = AssertableMockMvc.of(HelloController.class)
|
||||
MockMvcTester mockMvc = MockMvcTester.of(HelloController.class)
|
||||
.withHttpMessageConverters(List.of(mock(), mock(), converter));
|
||||
assertThat(mockMvc.perform(get("/json"))).hasStatusOk().body().jsonPath()
|
||||
assertThat(mockMvc.perform(get("/json"))).hasStatusOk().bodyJson()
|
||||
.extractingPath("$").convertTo(Message.class).satisfies(message -> {
|
||||
assertThat(message.message()).isEqualTo("Hello World");
|
||||
assertThat(message.counter()).isEqualTo(42);
|
||||
|
|
@ -132,11 +132,11 @@ class AssertableMockMvcTests {
|
|||
|
||||
@Test
|
||||
void performWithUnresolvedExceptionSetsException() {
|
||||
AssertableMockMvc mockMvc = AssertableMockMvc.of(HelloController.class);
|
||||
AssertableMvcResult result = mockMvc.perform(get("/error"));
|
||||
MockMvcTester mockMvc = MockMvcTester.of(HelloController.class);
|
||||
MvcTestResult result = mockMvc.perform(get("/error"));
|
||||
assertThat(result.getUnresolvedException()).isInstanceOf(ServletException.class)
|
||||
.cause().isInstanceOf(IllegalStateException.class).hasMessage("Expected");
|
||||
assertThat(result).hasFieldOrPropertyWithValue("target", null);
|
||||
assertThat(result).hasFieldOrPropertyWithValue("mvcResult", null);
|
||||
}
|
||||
|
||||
private GenericWebApplicationContext create(Class<?>... classes) {
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.web.servlet.assertj;
|
||||
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.assertj.core.api.AssertProvider;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.test.json.JsonContent;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ResponseBodyAssert}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class ResponseBodyAssertTests {
|
||||
|
||||
@Test
|
||||
void isEqualToWithByteArray() {
|
||||
MockHttpServletResponse response = createResponse("hello");
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
assertThat(fromResponse(response)).isEqualTo("hello".getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWithString() {
|
||||
MockHttpServletResponse response = createResponse("hello");
|
||||
assertThat(fromResponse(response)).isEqualTo("hello");
|
||||
}
|
||||
|
||||
@Test
|
||||
void jsonPathWithJsonResponseShouldPass() {
|
||||
MockHttpServletResponse response = createResponse("{\"message\": \"hello\"}");
|
||||
assertThat(fromResponse(response)).jsonPath().extractingPath("$.message").isEqualTo("hello");
|
||||
}
|
||||
|
||||
@Test
|
||||
void jsonPathWithJsonCompatibleResponseShouldPass() {
|
||||
MockHttpServletResponse response = createResponse("{\"albumById\": {\"name\": \"Greatest hits\"}}");
|
||||
assertThat(fromResponse(response)).jsonPath()
|
||||
.extractingPath("$.albumById.name").isEqualTo("Greatest hits");
|
||||
}
|
||||
|
||||
@Test
|
||||
void jsonCanLoadResourceRelativeToClass() {
|
||||
MockHttpServletResponse response = createResponse("{ \"name\" : \"Spring\", \"age\" : 123 }");
|
||||
// See org/springframework/test/json/example.json
|
||||
assertThat(fromResponse(response)).json(JsonContent.class).isLenientlyEqualTo("example.json");
|
||||
}
|
||||
|
||||
private MockHttpServletResponse createResponse(String body) {
|
||||
try {
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
response.getWriter().print(body);
|
||||
return response;
|
||||
}
|
||||
catch (UnsupportedEncodingException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private AssertProvider<ResponseBodyAssert> fromResponse(MockHttpServletResponse response) {
|
||||
return () -> new ResponseBodyAssert(response.getContentAsByteArray(), Charset.forName(response.getCharacterEncoding()), null);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue