Merge JSON assertions in a single Assert type
This commit merges the JSONCompare and JsonPath support in a single assert object. The rationale for the previous situation was that JsonPath is optional but merging the assertions methods do not make it mandatory as the usage if JsonPath is triggered only if it is actually used. See gh-32712
This commit is contained in:
parent
645556a28c
commit
24cc77655f
|
|
@ -0,0 +1,501 @@
|
|||
/*
|
||||
* 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}.
|
||||
*
|
||||
* @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;
|
||||
|
||||
private final 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}.
|
||||
* <p>Resources to match can be loaded relative to the given
|
||||
* {@code resourceLoadClass}. If not specified, resources must always be
|
||||
* absolute. A specific {@link Charset} can be provided if {@code UTF-8} is
|
||||
* not suitable.
|
||||
* @param json the JSON document to assert
|
||||
* @param jsonMessageConverter the converter to use
|
||||
* @param resourceLoadClass the class used to load resources
|
||||
* @param charset the charset of the JSON resources
|
||||
* @param selfType the implementation type of this assert
|
||||
*/
|
||||
protected AbstractJsonContentAssert(@Nullable String json,
|
||||
@Nullable GenericHttpMessageConverter<Object> jsonMessageConverter, @Nullable Class<?> resourceLoadClass,
|
||||
@Nullable Charset charset, Class<?> selfType) {
|
||||
super(json, selfType);
|
||||
this.jsonMessageConverter = jsonMessageConverter;
|
||||
this.jsonLoader = new JsonLoader(resourceLoadClass, charset);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
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, this.resourceLoadClass, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -16,351 +16,36 @@
|
|||
|
||||
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
|
||||
* 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}.
|
||||
* <p>Resources to match can be loaded relative to the given
|
||||
* {@code resourceLoadClass}. If not specified, resources must always be
|
||||
* absolute. A specific {@link Charset} can be provided if {@code UTF-8} is
|
||||
* not suitable.
|
||||
* @param json the JSON document to assert
|
||||
* @param jsonMessageConverter the converter to use
|
||||
* @param resourceLoadClass the class used to load resources
|
||||
* @param charset the charset of the JSON resources
|
||||
*/
|
||||
public JsonContentAssert(@Nullable CharSequence json, @Nullable Class<?> resourceLoadClass,
|
||||
@Nullable Charset charset) {
|
||||
public JsonContentAssert(@Nullable String json, @Nullable GenericHttpMessageConverter<Object> jsonMessageConverter,
|
||||
@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;
|
||||
super(json, jsonMessageConverter, resourceLoadClass, charset, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -25,8 +25,8 @@ 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.AbstractJsonContentAssert;
|
||||
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
|
||||
|
|
@ -53,19 +53,20 @@ public class ResponseBodyAssert extends AbstractByteArrayAssert<ResponseBodyAsse
|
|||
}
|
||||
|
||||
/**
|
||||
* Return a new {@linkplain JsonPathAssert assertion} object that provides
|
||||
* {@linkplain com.jayway.jsonpath.JsonPath JSON path} assertions on the
|
||||
* response body.
|
||||
* Return a new {@linkplain AbstractJsonContentAssert 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);
|
||||
public AbstractJsonContentAssert<?> jsonPath() {
|
||||
return new JsonContentAssert(getJson(), this.jsonMessageConverter, null, this.characterEncoding)
|
||||
.as("JSON body");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Return a new {@linkplain AbstractJsonContentAssert 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.
|
||||
|
|
@ -76,15 +77,15 @@ public class ResponseBodyAssert extends AbstractByteArrayAssert<ResponseBodyAsse
|
|||
* .isStrictlyEqualToJson("/com/acme/web/person/person-created.json");
|
||||
* </code></pre>
|
||||
*/
|
||||
public JsonContentAssert json() {
|
||||
public AbstractJsonContentAssert<?> 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.
|
||||
* Return a new {@linkplain AbstractJsonContentAssert 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'>
|
||||
|
|
@ -96,8 +97,9 @@ public class ResponseBodyAssert extends AbstractByteArrayAssert<ResponseBodyAsse
|
|||
* @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);
|
||||
public AbstractJsonContentAssert<?> json(@Nullable Class<?> resourceLoadClass) {
|
||||
return new JsonContentAssert(getJson(), this.jsonMessageConverter, resourceLoadClass, this.characterEncoding)
|
||||
.as("JSON body");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,781 @@
|
|||
/*
|
||||
* 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.charset.Charset;
|
||||
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, null, null);
|
||||
}
|
||||
|
||||
private AssertProvider<AbstractJsonContentAssert<?>> forJson(@Nullable String json, GenericHttpMessageConverter<Object> jsonHttpMessageConverter) {
|
||||
return () -> new TestJsonContentAssert(json, jsonHttpMessageConverter, null, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
private AssertProvider<AbstractJsonContentAssert<?>> forJson(@Nullable String json) {
|
||||
return () -> new TestJsonContentAssert(json, null, getClass(), null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
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, null, null);
|
||||
}
|
||||
|
||||
private static class TestJsonContentAssert extends AbstractJsonContentAssert<TestJsonContentAssert> {
|
||||
|
||||
public TestJsonContentAssert(@Nullable String json, @Nullable GenericHttpMessageConverter<Object> jsonMessageConverter, @Nullable Class<?> resourceLoadClass, @Nullable Charset charset) {
|
||||
super(json, jsonMessageConverter, resourceLoadClass, charset, 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -99,9 +99,9 @@ class AssertableMockMvcTests {
|
|||
@Test
|
||||
void createWithControllersHasNoHttpMessageConverter() {
|
||||
AssertableMockMvc mockMvc = AssertableMockMvc.of(new HelloController());
|
||||
JsonPathAssert jsonPathAssert = assertThat(mockMvc.perform(get("/json"))).hasStatusOk().body().jsonPath();
|
||||
AbstractJsonContentAssert<?> jsonContentAssert = assertThat(mockMvc.perform(get("/json"))).hasStatusOk().body().jsonPath();
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> jsonPathAssert.extractingPath("$").convertTo(Message.class))
|
||||
.isThrownBy(() -> jsonContentAssert.extractingPath("$").convertTo(Message.class))
|
||||
.withMessageContaining("No JSON message converter available");
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue