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:
Stéphane Nicoll
2024-05-06 11:08:02 +02:00
parent 645556a28c
commit 24cc77655f
9 changed files with 1320 additions and 1323 deletions

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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");
}