Browse Source

Introduce JSON comparison abstraction for tests

This commit introduces a JsonComparator abstraction, with an
implementation using JSONAssert. Previously, JSONAssert was the only
choice.

Test APIs have been adapted to allow the new abstraction while relying
on JSONAssert still for high-level methods.

Closes gh-32791

Co-authored-by: Stéphane Nicoll <stephane.nicoll@broadcom.com>
pull/32880/head
Phillip Webb 2 years ago committed by Stéphane Nicoll
parent
commit
d8dcd3af0b
  1. 124
      spring-test/src/main/java/org/springframework/test/json/AbstractJsonContentAssert.java
  2. 93
      spring-test/src/main/java/org/springframework/test/json/JsonAssert.java
  3. 54
      spring-test/src/main/java/org/springframework/test/json/JsonComparator.java
  4. 37
      spring-test/src/main/java/org/springframework/test/json/JsonCompareMode.java
  5. 91
      spring-test/src/main/java/org/springframework/test/json/JsonComparison.java
  6. 5
      spring-test/src/main/java/org/springframework/test/util/JsonExpectationsHelper.java
  7. 43
      spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java
  8. 18
      spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java
  9. 35
      spring-test/src/main/java/org/springframework/test/web/reactive/server/WebTestClient.java
  10. 50
      spring-test/src/main/java/org/springframework/test/web/servlet/result/ContentResultMatchers.java
  11. 14
      spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/ContentResultMatchersDsl.kt
  12. 83
      spring-test/src/test/java/org/springframework/test/json/AbstractJsonContentAssertTests.java
  13. 5
      spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/JsonContentTests.java
  14. 5
      spring-test/src/test/kotlin/org/springframework/test/web/servlet/MockMvcExtensionsTests.kt

124
spring-test/src/main/java/org/springframework/test/json/AbstractJsonContentAssert.java

@ -28,10 +28,6 @@ import org.assertj.core.api.AbstractStringAssert; @@ -28,10 +28,6 @@ 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;
@ -41,7 +37,6 @@ import org.springframework.core.io.Resource; @@ -41,7 +37,6 @@ 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 {@linkplain org.assertj.core.api.Assert assertions} that can be
@ -51,8 +46,8 @@ import org.springframework.util.function.ThrowingBiFunction; @@ -51,8 +46,8 @@ import org.springframework.util.function.ThrowingBiFunction;
* extracting a part of the document for further {@linkplain JsonPathValueAssert
* assertions} on the value.
*
* <p>Also supports comparing the JSON document against a target, using
* {@linkplain JSONCompare JSON Assert}. Resources that are loaded from
* <p>Also supports comparing the JSON document against a target, using a
* {@linkplain JsonComparator JSON Comparator}. Resources that are loaded from
* the classpath can be relative if a {@linkplain #withResourceLoadClass(Class)
* class} is provided. By default, {@code UTF-8} is used to load resources,
* but this can be overridden using {@link #withCharset(Charset)}.
@ -154,9 +149,9 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent @@ -154,9 +149,9 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
* the expected JSON
* @param compareMode the compare mode used when checking
*/
public SELF isEqualTo(@Nullable CharSequence expected, JSONCompareMode compareMode) {
public SELF isEqualTo(@Nullable CharSequence expected, JsonCompareMode compareMode) {
String expectedJson = this.jsonLoader.getJson(expected);
return assertNotFailed(compare(expectedJson, compareMode));
return assertIsMatch(compare(expectedJson, compareMode));
}
/**
@ -171,9 +166,9 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent @@ -171,9 +166,9 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
* @param expected a resource containing the expected JSON
* @param compareMode the compare mode used when checking
*/
public SELF isEqualTo(Resource expected, JSONCompareMode compareMode) {
public SELF isEqualTo(Resource expected, JsonCompareMode compareMode) {
String expectedJson = this.jsonLoader.getJson(expected);
return assertNotFailed(compare(expectedJson, compareMode));
return assertIsMatch(compare(expectedJson, compareMode));
}
/**
@ -184,9 +179,9 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent @@ -184,9 +179,9 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
* the expected JSON
* @param comparator the comparator used when checking
*/
public SELF isEqualTo(@Nullable CharSequence expected, JSONComparator comparator) {
public SELF isEqualTo(@Nullable CharSequence expected, JsonComparator comparator) {
String expectedJson = this.jsonLoader.getJson(expected);
return assertNotFailed(compare(expectedJson, comparator));
return assertIsMatch(compare(expectedJson, comparator));
}
/**
@ -201,13 +196,13 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent @@ -201,13 +196,13 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
* @param expected a resource containing the expected JSON
* @param comparator the comparator used when checking
*/
public SELF isEqualTo(Resource expected, JSONComparator comparator) {
public SELF isEqualTo(Resource expected, JsonComparator comparator) {
String expectedJson = this.jsonLoader.getJson(expected);
return assertNotFailed(compare(expectedJson, comparator));
return assertIsMatch(compare(expectedJson, comparator));
}
/**
* Verify that the actual value is {@link JSONCompareMode#LENIENT leniently}
* 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.
@ -215,11 +210,11 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent @@ -215,11 +210,11 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
* the expected JSON
*/
public SELF isLenientlyEqualTo(@Nullable CharSequence expected) {
return isEqualTo(expected, JSONCompareMode.LENIENT);
return isEqualTo(expected, JsonCompareMode.LENIENT);
}
/**
* Verify that the actual value is {@link JSONCompareMode#LENIENT leniently}
* 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>
@ -231,11 +226,11 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent @@ -231,11 +226,11 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
* @param expected a resource containing the expected JSON
*/
public SELF isLenientlyEqualTo(Resource expected) {
return isEqualTo(expected, JSONCompareMode.LENIENT);
return isEqualTo(expected, JsonCompareMode.LENIENT);
}
/**
* Verify that the actual value is {@link JSONCompareMode#STRICT strictly}
* 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.
@ -243,11 +238,11 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent @@ -243,11 +238,11 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
* the expected JSON
*/
public SELF isStrictlyEqualTo(@Nullable CharSequence expected) {
return isEqualTo(expected, JSONCompareMode.STRICT);
return isEqualTo(expected, JsonCompareMode.STRICT);
}
/**
* Verify that the actual value is {@link JSONCompareMode#STRICT strictly}
* 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>
@ -259,7 +254,7 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent @@ -259,7 +254,7 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
* @param expected a resource containing the expected JSON
*/
public SELF isStrictlyEqualTo(Resource expected) {
return isEqualTo(expected, JSONCompareMode.STRICT);
return isEqualTo(expected, JsonCompareMode.STRICT);
}
/**
@ -270,9 +265,9 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent @@ -270,9 +265,9 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
* the expected JSON
* @param compareMode the compare mode used when checking
*/
public SELF isNotEqualTo(@Nullable CharSequence expected, JSONCompareMode compareMode) {
public SELF isNotEqualTo(@Nullable CharSequence expected, JsonCompareMode compareMode) {
String expectedJson = this.jsonLoader.getJson(expected);
return assertNotPassed(compare(expectedJson, compareMode));
return assertIsMismatch(compare(expectedJson, compareMode));
}
/**
@ -287,9 +282,9 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent @@ -287,9 +282,9 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
* @param expected a resource containing the expected JSON
* @param compareMode the compare mode used when checking
*/
public SELF isNotEqualTo(Resource expected, JSONCompareMode compareMode) {
public SELF isNotEqualTo(Resource expected, JsonCompareMode compareMode) {
String expectedJson = this.jsonLoader.getJson(expected);
return assertNotPassed(compare(expectedJson, compareMode));
return assertIsMismatch(compare(expectedJson, compareMode));
}
/**
@ -300,9 +295,9 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent @@ -300,9 +295,9 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
* the expected JSON
* @param comparator the comparator used when checking
*/
public SELF isNotEqualTo(@Nullable CharSequence expected, JSONComparator comparator) {
public SELF isNotEqualTo(@Nullable CharSequence expected, JsonComparator comparator) {
String expectedJson = this.jsonLoader.getJson(expected);
return assertNotPassed(compare(expectedJson, comparator));
return assertIsMismatch(compare(expectedJson, comparator));
}
/**
@ -317,13 +312,13 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent @@ -317,13 +312,13 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
* @param expected a resource containing the expected JSON
* @param comparator the comparator used when checking
*/
public SELF isNotEqualTo(Resource expected, JSONComparator comparator) {
public SELF isNotEqualTo(Resource expected, JsonComparator comparator) {
String expectedJson = this.jsonLoader.getJson(expected);
return assertNotPassed(compare(expectedJson, comparator));
return assertIsMismatch(compare(expectedJson, comparator));
}
/**
* Verify that the actual value is not {@link JSONCompareMode#LENIENT
* 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.
@ -331,11 +326,11 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent @@ -331,11 +326,11 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
* the expected JSON
*/
public SELF isNotLenientlyEqualTo(@Nullable CharSequence expected) {
return isNotEqualTo(expected, JSONCompareMode.LENIENT);
return isNotEqualTo(expected, JsonCompareMode.LENIENT);
}
/**
* Verify that the actual value is not {@link 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>
@ -347,11 +342,11 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent @@ -347,11 +342,11 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
* @param expected a resource containing the expected JSON
*/
public SELF isNotLenientlyEqualTo(Resource expected) {
return isNotEqualTo(expected, JSONCompareMode.LENIENT);
return isNotEqualTo(expected, JsonCompareMode.LENIENT);
}
/**
* Verify that the actual value is not {@link JSONCompareMode#STRICT
* 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.
@ -359,11 +354,11 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent @@ -359,11 +354,11 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
* the expected JSON
*/
public SELF isNotStrictlyEqualTo(@Nullable CharSequence expected) {
return isNotEqualTo(expected, JSONCompareMode.STRICT);
return isNotEqualTo(expected, JsonCompareMode.STRICT);
}
/**
* Verify that the actual value is not {@link 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>
@ -375,7 +370,7 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent @@ -375,7 +370,7 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
* @param expected a resource containing the expected JSON
*/
public SELF isNotStrictlyEqualTo(Resource expected) {
return isNotEqualTo(expected, JSONCompareMode.STRICT);
return isNotEqualTo(expected, JsonCompareMode.STRICT);
}
/**
@ -405,54 +400,25 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent @@ -405,54 +400,25 @@ public abstract class AbstractJsonContentAssert<SELF extends AbstractJsonContent
}
private JSONCompareResult compare(@Nullable CharSequence expectedJson, JSONCompareMode compareMode) {
return compare(this.actual, expectedJson, (actualJsonString, expectedJsonString) ->
JSONCompare.compareJSON(expectedJsonString, actualJsonString, compareMode));
private JsonComparison compare(@Nullable CharSequence expectedJson, JsonCompareMode compareMode) {
return compare(expectedJson, JsonAssert.comparator(compareMode));
}
private JSONCompareResult compare(@Nullable CharSequence expectedJson, JSONComparator comparator) {
return compare(this.actual, expectedJson, (actualJsonString, expectedJsonString) ->
JSONCompare.compareJSON(expectedJsonString, actualJsonString, comparator));
private JsonComparison compare(@Nullable CharSequence expectedJson, JsonComparator comparator) {
return comparator.compare((expectedJson != null) ? expectedJson.toString() : null, this.actual);
}
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 SELF assertIsMatch(JsonComparison result) {
return assertComparison(result, JsonComparison.Result.MATCH);
}
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 assertIsMismatch(JsonComparison result) {
return assertComparison(result, JsonComparison.Result.MISMATCH);
}
private SELF assertNotPassed(JSONCompareResult result) {
if (result.passed()) {
failWithMessage("JSON comparison failure: %s", result.getMessage());
private SELF assertComparison(JsonComparison jsonComparison, JsonComparison.Result requiredResult) {
if (jsonComparison.getResult() != requiredResult) {
failWithMessage("JSON comparison failure: %s", jsonComparison.getMessage());
}
return this.myself;
}

93
spring-test/src/main/java/org/springframework/test/json/JsonAssert.java

@ -0,0 +1,93 @@ @@ -0,0 +1,93 @@
/*
* 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 org.skyscreamer.jsonassert.JSONCompare;
import org.skyscreamer.jsonassert.JSONCompareMode;
import org.skyscreamer.jsonassert.JSONCompareResult;
import org.skyscreamer.jsonassert.comparator.JSONComparator;
import org.springframework.lang.Nullable;
import org.springframework.util.function.ThrowingBiFunction;
/**
* Useful methods that can be used with {@code org.skyscreamer.jsonassert}.
*
* @author Phillip Webb
* @since 6.2
*/
public abstract class JsonAssert {
/**
* Create a {@link JsonComparator} from the given {@link JsonCompareMode}.
* @param compareMode the mode to use
* @return a new {@link JsonComparator} instance
* @see JSONCompareMode#STRICT
* @see JSONCompareMode#LENIENT
*/
public static JsonComparator comparator(JsonCompareMode compareMode) {
return comparator(toJSONCompareMode(compareMode));
}
/**
* Create a new {@link JsonComparator} from the given JSONAssert
* {@link JSONComparator}.
* @param comparator the JSON Assert {@link JSONComparator}
* @return a new {@link JsonComparator} instance
*/
public static JsonComparator comparator(JSONComparator comparator) {
return comparator((expectedJson, actualJson) -> JSONCompare
.compareJSON(expectedJson, actualJson, comparator));
}
/**
* Create a new {@link JsonComparator} from the given JSONAssert
* {@link JSONCompareMode}.
* @param mode the JSON Assert {@link JSONCompareMode}
* @return a new {@link JsonComparator} instance
*/
public static JsonComparator comparator(JSONCompareMode mode) {
return comparator((expectedJson, actualJson) -> JSONCompare
.compareJSON(expectedJson, actualJson, mode));
}
private static JsonComparator comparator(ThrowingBiFunction<String, String, JSONCompareResult> compareFunction) {
return (expectedJson, actualJson) -> compare(expectedJson, actualJson, compareFunction);
}
private static JsonComparison compare(@Nullable String expectedJson, @Nullable String actualJson,
ThrowingBiFunction<String, String, JSONCompareResult> compareFunction) {
if (actualJson == null) {
return (expectedJson != null)
? JsonComparison.mismatch("Expected null JSON")
: JsonComparison.match();
}
if (expectedJson == null) {
return JsonComparison.mismatch("Expected non-null JSON");
}
JSONCompareResult result = compareFunction.throwing(IllegalStateException::new).apply(expectedJson, actualJson);
return (!result.passed())
? JsonComparison.mismatch(result.getMessage())
: JsonComparison.match();
}
private static JSONCompareMode toJSONCompareMode(JsonCompareMode compareMode) {
return (compareMode != JsonCompareMode.LENIENT ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT);
}
}

54
spring-test/src/main/java/org/springframework/test/json/JsonComparator.java

@ -0,0 +1,54 @@ @@ -0,0 +1,54 @@
/*
* 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 org.springframework.lang.Nullable;
import org.springframework.test.json.JsonComparison.Result;
/**
* Strategy interface used to compare JSON strings.
*
* @author Phillip Webb
* @since 6.2
* @see JsonAssert
*/
@FunctionalInterface
public interface JsonComparator {
/**
* Compare the given JSON strings.
* @param expectedJson the expected JSON
* @param actualJson the actual JSON
* @return the JSON comparison
*/
JsonComparison compare(@Nullable String expectedJson, @Nullable String actualJson);
/**
* Assert that the {@code expectedJson} matches the comparison rules of ths
* instance against the {@code actualJson}. Throw an {@link AssertionError}
* if the comparison does not match.
* @param expectedJson the expected JSON
* @param actualJson the actual JSON
*/
default void assertIsMatch(@Nullable String expectedJson, @Nullable String actualJson) {
JsonComparison comparison = compare(expectedJson, actualJson);
if (comparison.getResult() == Result.MISMATCH) {
throw new AssertionError(comparison.getMessage());
}
}
}

37
spring-test/src/main/java/org/springframework/test/json/JsonCompareMode.java

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
/*
* 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;
/**
* Modes that can be used to compare JSON.
*
* @author Phillip Webb
* @since 6.2
*/
public enum JsonCompareMode {
/**
* Strict checking.
*/
STRICT,
/**
* Lenient checking.
*/
LENIENT
}

91
spring-test/src/main/java/org/springframework/test/json/JsonComparison.java

@ -0,0 +1,91 @@ @@ -0,0 +1,91 @@
/*
* 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 org.springframework.lang.Nullable;
/**
* A comparison of two JSON strings as returned from a {@link JsonComparator}.
*
* @author Phillip Webb
* @since 6.2
*/
public final class JsonComparison {
private final Result result;
@Nullable
private final String message;
private JsonComparison(Result result, @Nullable String message) {
this.result = result;
this.message = message;
}
/**
* Factory method to create a new {@link JsonComparison} when the JSON
* strings match.
* @return a new {@link JsonComparison} instance
*/
public static JsonComparison match() {
return new JsonComparison(Result.MATCH, null);
}
/**
* Factory method to create a new {@link JsonComparison} when the JSON strings
* do not match.
* @param message a message describing the mismatch
* @return a new {@link JsonComparison} instance
*/
public static JsonComparison mismatch(String message) {
return new JsonComparison(Result.MISMATCH, message);
}
/**
* Return the result of the comparison.
*/
public Result getResult() {
return this.result;
}
/**
* Return a message describing the comparison.
*/
@Nullable
public String getMessage() {
return this.message;
}
/**
* Comparison results.
*/
public enum Result {
/**
* The JSON strings match when considering the comparison rules.
*/
MATCH,
/**
* The JSON strings do not match when considering the comparison rules.
*/
MISMATCH
}
}

5
spring-test/src/main/java/org/springframework/test/util/JsonExpectationsHelper.java

@ -18,6 +18,8 @@ package org.springframework.test.util; @@ -18,6 +18,8 @@ package org.springframework.test.util;
import org.skyscreamer.jsonassert.JSONAssert;
import org.springframework.test.json.JsonComparator;
/**
* A helper class for assertions on JSON content.
*
@ -26,7 +28,10 @@ import org.skyscreamer.jsonassert.JSONAssert; @@ -26,7 +28,10 @@ import org.skyscreamer.jsonassert.JSONAssert;
*
* @author Sebastien Deleuze
* @since 4.1
* @deprecated in favor of using {@link JSONAssert} directly or the
* {@link JsonComparator} abstraction
*/
@Deprecated(since = "6.2")
public class JsonExpectationsHelper {
/**

43
spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java

@ -42,7 +42,10 @@ import org.springframework.http.converter.FormHttpMessageConverter; @@ -42,7 +42,10 @@ import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.mock.http.MockHttpInputMessage;
import org.springframework.mock.http.client.MockClientHttpRequest;
import org.springframework.test.util.JsonExpectationsHelper;
import org.springframework.test.json.JsonAssert;
import org.springframework.test.json.JsonComparator;
import org.springframework.test.json.JsonCompareMode;
import org.springframework.test.json.JsonComparison;
import org.springframework.test.util.XmlExpectationsHelper;
import org.springframework.test.web.client.RequestMatcher;
import org.springframework.util.LinkedMultiValueMap;
@ -71,8 +74,6 @@ public class ContentRequestMatchers { @@ -71,8 +74,6 @@ public class ContentRequestMatchers {
private final XmlExpectationsHelper xmlHelper;
private final JsonExpectationsHelper jsonHelper;
/**
* Class constructor, not for direct instantiation.
@ -80,7 +81,6 @@ public class ContentRequestMatchers { @@ -80,7 +81,6 @@ public class ContentRequestMatchers {
*/
protected ContentRequestMatchers() {
this.xmlHelper = new XmlExpectationsHelper();
this.jsonHelper = new JsonExpectationsHelper();
}
@ -331,7 +331,7 @@ public class ContentRequestMatchers { @@ -331,7 +331,7 @@ public class ContentRequestMatchers {
* @since 5.0.5
*/
public RequestMatcher json(String expectedJsonContent) {
return json(expectedJsonContent, false);
return json(expectedJsonContent, JsonCompareMode.LENIENT);
}
/**
@ -348,12 +348,43 @@ public class ContentRequestMatchers { @@ -348,12 +348,43 @@ public class ContentRequestMatchers {
* @param expectedJsonContent the expected JSON content
* @param strict enables strict checking
* @since 5.0.5
* @deprecated in favor of {@link #json(String, JsonCompareMode)}
*/
@Deprecated(since = "6.2")
public RequestMatcher json(String expectedJsonContent, boolean strict) {
JsonCompareMode compareMode = (strict ? JsonCompareMode.STRICT : JsonCompareMode.LENIENT);
return json(expectedJsonContent, compareMode);
}
/**
* Parse the request body and the given string as JSON and assert the two
* using the given {@linkplain JsonCompareMode mode}. If the comparison failed,
* throws an {@link AssertionError} with the message of the {@link JsonComparison}.
* <p>Use of this matcher requires the <a
* href="https://jsonassert.skyscreamer.org/">JSONassert</a> library.
* @param expectedJsonContent the expected JSON content
* @param compareMode the compare mode
* @since 6.2
*/
public RequestMatcher json(String expectedJsonContent, JsonCompareMode compareMode) {
return json(expectedJsonContent, JsonAssert.comparator(compareMode));
}
/**
* Parse the request body and the given string as JSON and assert the two
* using the given {@link JsonComparator}. If the comparison failed, throws an
* {@link AssertionError} with the message of the {@link JsonComparison}.
* <p>Use this matcher if you require a custom JSONAssert configuration or
* if you desire to use another assertion library.
* @param expectedJsonContent the expected JSON content
* @param comparator the comparator to use
* @since 6.2
*/
public RequestMatcher json(String expectedJsonContent, JsonComparator comparator) {
return request -> {
try {
MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
this.jsonHelper.assertJsonEqual(expectedJsonContent, mockRequest.getBodyAsString(), strict);
comparator.assertIsMatch(expectedJsonContent, mockRequest.getBodyAsString());
}
catch (Exception ex) {
throw new AssertionError("Failed to parse expected or actual JSON request content", ex);

18
spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java

@ -45,9 +45,11 @@ import org.springframework.http.MediaType; @@ -45,9 +45,11 @@ import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.ClientHttpRequest;
import org.springframework.lang.Nullable;
import org.springframework.test.json.JsonAssert;
import org.springframework.test.json.JsonComparator;
import org.springframework.test.json.JsonCompareMode;
import org.springframework.test.util.AssertionErrors;
import org.springframework.test.util.ExceptionCollector;
import org.springframework.test.util.JsonExpectationsHelper;
import org.springframework.test.util.XmlExpectationsHelper;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
@ -658,10 +660,22 @@ class DefaultWebTestClient implements WebTestClient { @@ -658,10 +660,22 @@ class DefaultWebTestClient implements WebTestClient {
}
@Override
@Deprecated(since = "6.2")
public BodyContentSpec json(String json, boolean strict) {
JsonCompareMode compareMode = (strict ? JsonCompareMode.STRICT : JsonCompareMode.LENIENT);
return json(json, compareMode);
}
@Override
public BodyContentSpec json(String expectedJson, JsonCompareMode compareMode) {
return json(expectedJson, JsonAssert.comparator(compareMode));
}
@Override
public BodyContentSpec json(String expectedJson, JsonComparator comparator) {
this.result.assertWithDiagnostics(() -> {
try {
new JsonExpectationsHelper().assertJsonEqual(json, getBodyAsString(), strict);
comparator.assertIsMatch(expectedJson, getBodyAsString());
}
catch (Exception ex) {
throw new AssertionError("JSON parsing error", ex);

35
spring-test/src/main/java/org/springframework/test/web/reactive/server/WebTestClient.java

@ -40,6 +40,9 @@ import org.springframework.http.client.reactive.ClientHttpRequest; @@ -40,6 +40,9 @@ import org.springframework.http.client.reactive.ClientHttpRequest;
import org.springframework.http.codec.ClientCodecConfigurer;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.lang.Nullable;
import org.springframework.test.json.JsonComparator;
import org.springframework.test.json.JsonCompareMode;
import org.springframework.test.json.JsonComparison;
import org.springframework.util.MultiValueMap;
import org.springframework.validation.Validator;
import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder;
@ -997,10 +1000,10 @@ public interface WebTestClient { @@ -997,10 +1000,10 @@ public interface WebTestClient {
* <a href="https://jsonassert.skyscreamer.org/">JSONassert</a> library
* to be on the classpath.
* @param expectedJson the expected JSON content
* @see #json(String, boolean)
* @see #json(String, JsonCompareMode)
*/
default BodyContentSpec json(String expectedJson) {
return json(expectedJson, false);
return json(expectedJson, JsonCompareMode.LENIENT);
}
/**
@ -1019,9 +1022,37 @@ public interface WebTestClient { @@ -1019,9 +1022,37 @@ public interface WebTestClient {
* @param strict enables strict checking if {@code true}
* @since 5.3.16
* @see #json(String)
* @deprecated in favor of {@link #json(String, JsonCompareMode)}
*/
@Deprecated(since = "6.2")
BodyContentSpec json(String expectedJson, boolean strict);
/**
* Parse the expected and actual response content as JSON and perform a
* comparison using the given {@linkplain JsonCompareMode mode}. If the
* comparison failed, throws an {@link AssertionError} with the message
* of the {@link JsonComparison}.
* <p>Use of this method requires the
* <a href="https://jsonassert.skyscreamer.org/">JSONassert</a> library
* to be on the classpath.
* @param expectedJson the expected JSON content
* @param compareMode the compare mode
* @since 6.2
* @see #json(String)
*/
BodyContentSpec json(String expectedJson, JsonCompareMode compareMode);
/**
* Parse the expected and actual response content as JSON and perform a
* comparison using the given {@link JsonComparator}. If the comparison
* failed, throws an {@link AssertionError} with the message of the
* {@link JsonComparison}.
* @param expectedJson the expected JSON content
* @param comparator the comparator to use
* @since 6.2
*/
BodyContentSpec json(String expectedJson, JsonComparator comparator);
/**
* Parse expected and actual response content as XML and assert that
* the two are "similar", i.e. they contain the same elements and

50
spring-test/src/main/java/org/springframework/test/web/servlet/result/ContentResultMatchers.java

@ -28,7 +28,10 @@ import org.hamcrest.Matcher; @@ -28,7 +28,10 @@ import org.hamcrest.Matcher;
import org.w3c.dom.Node;
import org.springframework.http.MediaType;
import org.springframework.test.util.JsonExpectationsHelper;
import org.springframework.test.json.JsonAssert;
import org.springframework.test.json.JsonComparator;
import org.springframework.test.json.JsonCompareMode;
import org.springframework.test.json.JsonComparison;
import org.springframework.test.util.XmlExpectationsHelper;
import org.springframework.test.web.servlet.ResultMatcher;
@ -51,8 +54,6 @@ public class ContentResultMatchers { @@ -51,8 +54,6 @@ public class ContentResultMatchers {
private final XmlExpectationsHelper xmlHelper;
private final JsonExpectationsHelper jsonHelper;
/**
* Protected constructor.
@ -60,7 +61,6 @@ public class ContentResultMatchers { @@ -60,7 +61,6 @@ public class ContentResultMatchers {
*/
protected ContentResultMatchers() {
this.xmlHelper = new XmlExpectationsHelper();
this.jsonHelper = new JsonExpectationsHelper();
}
@ -198,13 +198,16 @@ public class ContentResultMatchers { @@ -198,13 +198,16 @@ public class ContentResultMatchers {
/**
* Parse the expected and actual strings as JSON and assert the two
* are "similar" - i.e. they contain the same attribute-value pairs
* regardless of formatting with a lenient checking (extensible, and non-strict array
* ordering).
* regardless of formatting with a lenient checking (extensible,
* and non-strict array ordering).
* <p>Use of this matcher requires the <a
* href="https://jsonassert.skyscreamer.org/">JSONassert</a> library.
* @param jsonContent the expected JSON content
* @since 4.1
* @see #json(String, JsonCompareMode)
*/
public ResultMatcher json(String jsonContent) {
return json(jsonContent, false);
return json(jsonContent, JsonCompareMode.LENIENT);
}
/**
@ -220,11 +223,42 @@ public class ContentResultMatchers { @@ -220,11 +223,42 @@ public class ContentResultMatchers {
* @param jsonContent the expected JSON content
* @param strict enables strict checking
* @since 4.2
* @deprecated in favor of {@link #json(String, JsonCompareMode)}
*/
@Deprecated(since = "6.2")
public ResultMatcher json(String jsonContent, boolean strict) {
JsonCompareMode compareMode = (strict ? JsonCompareMode.STRICT : JsonCompareMode.LENIENT);
return json(jsonContent, compareMode);
}
/**
* Parse the response content and the given string as JSON and assert the two
* using the given {@linkplain JsonCompareMode mode}. If the comparison failed,
* throws an {@link AssertionError} with the message of the {@link JsonComparison}.
* <p>Use of this matcher requires the <a
* href="https://jsonassert.skyscreamer.org/">JSONassert</a> library.
* @param jsonContent the expected JSON content
* @param compareMode the compare mode
* @since 6.2
*/
public ResultMatcher json(String jsonContent, JsonCompareMode compareMode) {
return json(jsonContent, JsonAssert.comparator(compareMode));
}
/**
* Parse the response content and the given string as JSON and assert the two
* using the given {@link JsonComparator}. If the comparison failed, throws an
* {@link AssertionError} with the message of the {@link JsonComparison}.
* <p>Use this matcher if you require a custom JSONAssert configuration or
* if you desire to use another assertion library.
* @param jsonContent the expected JSON content
* @param comparator the comparator to use
* @since 6.2
*/
public ResultMatcher json(String jsonContent, JsonComparator comparator) {
return result -> {
String content = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
this.jsonHelper.assertJsonEqual(jsonContent, content, strict);
comparator.assertIsMatch(jsonContent, content);
};
}

14
spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/ContentResultMatchersDsl.kt

@ -18,6 +18,7 @@ package org.springframework.test.web.servlet.result @@ -18,6 +18,7 @@ package org.springframework.test.web.servlet.result
import org.hamcrest.Matcher
import org.springframework.http.MediaType
import org.springframework.test.json.JsonCompareMode
import org.springframework.test.web.servlet.ResultActions
import org.w3c.dom.Node
import javax.xml.transform.Source
@ -112,7 +113,16 @@ class ContentResultMatchersDsl internal constructor (private val actions: Result @@ -112,7 +113,16 @@ class ContentResultMatchersDsl internal constructor (private val actions: Result
/**
* @see ContentResultMatchers.json
*/
fun json(jsonContent: String, strict: Boolean = false) {
actions.andExpect(matchers.json(jsonContent, strict))
@Deprecated(message = "Use JsonCompare mode instead")
fun json(jsonContent: String, strict: Boolean) {
val compareMode = (if (strict) JsonCompareMode.STRICT else JsonCompareMode.LENIENT)
actions.andExpect(matchers.json(jsonContent, compareMode))
}
/**
* @see ContentResultMatchers.json
*/
fun json(jsonContent: String, compareMode: JsonCompareMode = JsonCompareMode.LENIENT) {
actions.andExpect(matchers.json(jsonContent, compareMode))
}
}

83
spring-test/src/test/java/org/springframework/test/json/AbstractJsonContentAssertTests.java

@ -28,6 +28,8 @@ import java.util.stream.Stream; @@ -28,6 +28,8 @@ import java.util.stream.Stream;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.assertj.core.api.AssertProvider;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
@ -37,7 +39,7 @@ import org.junit.jupiter.params.provider.Arguments; @@ -37,7 +39,7 @@ 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.JSONCompareResult;
import org.skyscreamer.jsonassert.comparator.JSONComparator;
import org.springframework.core.ParameterizedTypeReference;
@ -55,6 +57,10 @@ import static org.assertj.core.api.Assertions.assertThat; @@ -55,6 +57,10 @@ 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;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link AbstractJsonContentAssert}.
@ -79,7 +85,7 @@ class AbstractJsonContentAssertTests { @@ -79,7 +85,7 @@ class AbstractJsonContentAssertTests {
private static final MappingJackson2HttpMessageConverter jsonHttpMessageConverter =
new MappingJackson2HttpMessageConverter(new ObjectMapper());
private static final JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT);
private static final JsonComparator comparator = JsonAssert.comparator(JsonCompareMode.LENIENT);
@Test
void isNullWhenActualIsNullShouldPass() {
@ -364,29 +370,29 @@ class AbstractJsonContentAssertTests { @@ -364,29 +370,29 @@ class AbstractJsonContentAssertTests {
void isEqualToWhenExpectedIsNullShouldFail() {
CharSequence actual = null;
assertThatExceptionOfType(AssertionError.class)
.isThrownBy(() -> assertThat(forJson(SOURCE)).isEqualTo(actual, JSONCompareMode.LENIENT));
.isThrownBy(() -> assertThat(forJson(SOURCE)).isEqualTo(actual, JsonCompareMode.LENIENT));
}
@Test
void isEqualToWhenStringIsMatchingAndLenientShouldPass() {
assertThat(forJson(SOURCE)).isEqualTo(LENIENT_SAME, JSONCompareMode.LENIENT);
assertThat(forJson(SOURCE)).isEqualTo(LENIENT_SAME, JsonCompareMode.LENIENT);
}
@Test
void isEqualToWhenStringIsNotMatchingAndLenientShouldFail() {
assertThatExceptionOfType(AssertionError.class)
.isThrownBy(() -> assertThat(forJson(SOURCE)).isEqualTo(DIFFERENT, JSONCompareMode.LENIENT));
.isThrownBy(() -> assertThat(forJson(SOURCE)).isEqualTo(DIFFERENT, JsonCompareMode.LENIENT));
}
@Test
void isEqualToWhenResourcePathIsMatchingAndLenientShouldPass() {
assertThat(forJson(SOURCE)).isEqualTo("lenient-same.json", JSONCompareMode.LENIENT);
assertThat(forJson(SOURCE)).isEqualTo("lenient-same.json", JsonCompareMode.LENIENT);
}
@Test
void isEqualToWhenResourcePathIsNotMatchingAndLenientShouldFail() {
assertThatExceptionOfType(AssertionError.class)
.isThrownBy(() -> assertThat(forJson(SOURCE)).isEqualTo("different.json", JSONCompareMode.LENIENT));
.isThrownBy(() -> assertThat(forJson(SOURCE)).isEqualTo("different.json", JsonCompareMode.LENIENT));
}
Stream<Arguments> source() {
@ -416,14 +422,14 @@ class AbstractJsonContentAssertTests { @@ -416,14 +422,14 @@ class AbstractJsonContentAssertTests {
@ParameterizedTest
@MethodSource("lenientSame")
void isEqualToWhenResourceIsMatchingAndLenientSameShouldPass(Resource expected) {
assertThat(forJson(SOURCE)).isEqualTo(expected, JSONCompareMode.LENIENT);
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));
() -> assertThat(forJson(SOURCE)).isEqualTo(expected, JsonCompareMode.LENIENT));
}
@Test
@ -568,36 +574,36 @@ class AbstractJsonContentAssertTests { @@ -568,36 +574,36 @@ class AbstractJsonContentAssertTests {
@Test
void isNotEqualToWhenStringIsMatchingAndLenientShouldFail() {
assertThatExceptionOfType(AssertionError.class)
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotEqualTo(LENIENT_SAME, JSONCompareMode.LENIENT));
.isThrownBy(() -> assertThat(forJson(SOURCE)).isNotEqualTo(LENIENT_SAME, JsonCompareMode.LENIENT));
}
@Test
void isNotEqualToWhenStringIsNotMatchingAndLenientShouldPass() {
assertThat(forJson(SOURCE)).isNotEqualTo(DIFFERENT, JSONCompareMode.LENIENT);
assertThat(forJson(SOURCE)).isNotEqualTo(DIFFERENT, JsonCompareMode.LENIENT);
}
@Test
void isNotEqualToWhenResourcePathIsMatchingAndLenientShouldFail() {
assertThatExceptionOfType(AssertionError.class).isThrownBy(
() -> assertThat(forJson(SOURCE)).isNotEqualTo("lenient-same.json", JSONCompareMode.LENIENT));
() -> assertThat(forJson(SOURCE)).isNotEqualTo("lenient-same.json", JsonCompareMode.LENIENT));
}
@Test
void isNotEqualToWhenResourcePathIsNotMatchingAndLenientShouldPass() {
assertThat(forJson(SOURCE)).isNotEqualTo("different.json", JSONCompareMode.LENIENT);
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));
.isNotEqualTo(expected, JsonCompareMode.LENIENT));
}
@ParameterizedTest
@MethodSource("different")
void isNotEqualToWhenResourceIsNotMatchingAndLenientShouldPass(Resource expected) {
assertThat(forJson(SOURCE)).isNotEqualTo(expected, JSONCompareMode.LENIENT);
assertThat(forJson(SOURCE)).isNotEqualTo(expected, JsonCompareMode.LENIENT);
}
@Test
@ -715,6 +721,28 @@ class AbstractJsonContentAssertTests { @@ -715,6 +721,28 @@ class AbstractJsonContentAssertTests {
assertThat(forJson(SOURCE)).isNotStrictlyEqualTo(expected);
}
@Test
void isEqualToWithCustomCompareMode() {
String differentOrder = """
{
"spring": [
"framework",
"boot"
]
}
""";
assertThat(forJson(SOURCE)).isEqualTo(differentOrder, JsonAssert.comparator(JSONCompareMode.NON_EXTENSIBLE));
}
@Test
void isEqualToWithCustomJsonComparator() throws JSONException {
String empty = "{}";
JSONComparator comparator = mock(JSONComparator.class);
given(comparator.compareJSON(any(JSONObject.class), any(JSONObject.class))).willReturn(new JSONCompareResult());
assertThat(forJson(SOURCE)).isEqualTo(empty, JsonAssert.comparator(comparator));
verify(comparator).compareJSON(any(JSONObject.class), any(JSONObject.class));
}
@Test
void withResourceLoadClassShouldAllowToLoadRelativeContent() {
AbstractJsonContentAssert<?> jsonAssert = assertThat(forJson(NULLS)).withResourceLoadClass(String.class);
@ -730,6 +758,31 @@ class AbstractJsonContentAssertTests { @@ -730,6 +758,31 @@ class AbstractJsonContentAssertTests {
}
}
@Nested
class JsonComparatorTests {
private final JsonComparator comparator = mock(JsonComparator.class);
@Test
void isEqualToInvokesComparator() {
given(comparator.compare("{ }", "{}")).willReturn(JsonComparison.match());
assertThat(forJson("{}")).isEqualTo("{ }", this.comparator);
verify(comparator).compare("{ }", "{}");
}
@Test
void isEqualToWithNoMatchProvidesErrorMessage() {
given(comparator.compare("{ }", "{}")).willReturn(JsonComparison.mismatch("No additional whitespace expected"));
assertThatExceptionOfType(AssertionError.class)
.isThrownBy(() -> assertThat(forJson("{}")).isEqualTo("{ }", this.comparator))
.withMessageContaining("No additional whitespace expected");
verify(comparator).compare("{ }", "{}");
}
private AssertProvider<AbstractJsonContentAssert<?>> forJson(@Nullable String json) {
return () -> new TestJsonContentAssert(json, null).withResourceLoadClass(getClass());
}
}
private Consumer<AssertionError> hasFailedToMatchPath(String expression) {
return error -> assertThat(error.getMessage()).containsSubsequence("Expecting:",

5
spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/JsonContentTests.java

@ -23,6 +23,7 @@ import reactor.core.publisher.Flux; @@ -23,6 +23,7 @@ import reactor.core.publisher.Flux;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.json.JsonCompareMode;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@ -74,7 +75,7 @@ class JsonContentTests { @@ -74,7 +75,7 @@ class JsonContentTests {
{"firstName":"John", "lastName":"Smith"}
]
""",
true);
JsonCompareMode.STRICT);
}
@Test
@ -89,7 +90,7 @@ class JsonContentTests { @@ -89,7 +90,7 @@ class JsonContentTests {
{"firstName":"John"}
]
""",
true)
JsonCompareMode.STRICT)
);
}

5
spring-test/src/test/kotlin/org/springframework/test/web/servlet/MockMvcExtensionsTests.kt

@ -27,6 +27,7 @@ import org.springframework.http.MediaType.APPLICATION_ATOM_XML @@ -27,6 +27,7 @@ import org.springframework.http.MediaType.APPLICATION_ATOM_XML
import org.springframework.http.MediaType.APPLICATION_JSON
import org.springframework.http.MediaType.APPLICATION_XML
import org.springframework.http.MediaType.TEXT_PLAIN
import org.springframework.test.json.JsonCompareMode
import org.springframework.test.web.Person
import org.springframework.test.web.servlet.setup.MockMvcBuilders
import org.springframework.web.bind.annotation.GetMapping
@ -64,7 +65,7 @@ class MockMvcExtensionsTests { @@ -64,7 +65,7 @@ class MockMvcExtensionsTests {
status { isOk() }
content { contentType(APPLICATION_JSON) }
jsonPath("$.name") { value("Lee") }
content { json("""{"someBoolean": false}""", false) }
content { json("""{"someBoolean": false}""", JsonCompareMode.LENIENT) }
}.andDo {
print()
}
@ -130,7 +131,7 @@ class MockMvcExtensionsTests { @@ -130,7 +131,7 @@ class MockMvcExtensionsTests {
status { isOk() }
content { contentType(APPLICATION_JSON) }
jsonPath("$.name") { value("Lee") }
content { json("""{"someBoolean": false}""", false) }
content { json("""{"someBoolean": false}""", JsonCompareMode.LENIENT) }
}.andDo {
print()
}

Loading…
Cancel
Save