Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#767 Basic support for Spring JsonComparator #772

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import net.javacrumbs.jsonunit.assertj.internal.JsonRepresentation;
import net.javacrumbs.jsonunit.core.Configuration;
import net.javacrumbs.jsonunit.core.internal.JsonUtils;
import org.assertj.core.api.AssertFactory;
import org.assertj.core.api.Assertions;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -75,8 +76,19 @@ public static Object value(Object input) {
return new ExpectedNode(JsonUtils.wrapDeserializedObject(input));
}

public static AssertFactory<Object, ConfigurableJsonAssert> jsonUnitAssert() {
return new JsonUnitAssertFactory();
}

@FunctionalInterface
public interface JsonAssertionCallback {
void doAssert(@NotNull ConfigurableJsonAssert assertion);
}

private static class JsonUnitAssertFactory implements AssertFactory<Object, ConfigurableJsonAssert> {
@Override
public ConfigurableJsonAssert createAssert(Object actual) {
return new ConfigurableJsonAssert(actual, Configuration.empty());
}
}
}
18 changes: 17 additions & 1 deletion json-unit-spring/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
Spring Mock assertions
</description>
<properties>
<spring.version>6.1.8</spring.version>
<spring.version>6.2.0-SNAPSHOT</spring.version>
<kotlin.compiler.jvmTarget>17</kotlin.compiler.jvmTarget>
<osgi.exportPackage>net.javacrumbs.jsonunit.spring</osgi.exportPackage>
</properties>
Expand Down Expand Up @@ -107,6 +107,22 @@

</dependencies>

<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<repository>
<id>spring-milestone</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>

<build>
<plugins>
<plugin>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package net.javacrumbs.jsonunit.spring;

import static net.javacrumbs.jsonunit.spring.Utils.getCharset;

import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import net.javacrumbs.jsonunit.core.Configuration;
import net.javacrumbs.jsonunit.core.internal.Diff;
import org.jetbrains.annotations.NotNull;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractGenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.test.json.JsonComparator;
import org.springframework.test.json.JsonComparison;
import org.springframework.util.FileCopyUtils;

/**
* Implements Spring's JsonComparator. The integration API is pretty limited, so
* you are better off using JsonUnit directly.
*/
public class JsonUnitJsonComparator implements JsonComparator {

private final Configuration configuration;

private JsonUnitJsonComparator(Configuration configuration) {
this.configuration = configuration;
}

/**
* Creates JsonUnit backed JsonComparator with given configuration.
*/
public static JsonComparator comparator(Configuration configuration) {
return new JsonUnitJsonComparator(configuration);
}

/**
* Creates JsonUnit backed JsonComparator
*/
public static JsonComparator comparator() {
return new JsonUnitJsonComparator(Configuration.empty());
}

public static HttpMessageConverter<Object> jsonUnitMessageConverter() {
return new DirectGenericHttpMessageConverter();
}

@Override
public @NotNull JsonComparison compare(@NotNull String expectedJson, @NotNull String actualJson) {
Diff diff = Diff.create(expectedJson, actualJson, "actual", "", configuration);
if (diff.similar()) {
return JsonComparison.match();
} else {
return JsonComparison.mismatch(diff.differences());
}
}

private static class DirectGenericHttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> {
public DirectGenericHttpMessageConverter() {
super(MediaType.APPLICATION_JSON, new MediaType("application", "*+json"));
}

@Override
public @NotNull Object read(
@NotNull Type type, @NotNull Class<?> contextClass, @NotNull HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
return readInternal(contextClass, inputMessage);
}

@Override
protected @NotNull Object readInternal(@NotNull Class<?> clazz, @NotNull HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
return FileCopyUtils.copyToString(new InputStreamReader(inputMessage.getBody(), getCharset(inputMessage)));
}

@Override
protected void writeInternal(@NotNull Object o, @NotNull Type type, @NotNull HttpOutputMessage outputMessage)
throws HttpMessageNotWritableException {
throw new UnsupportedOperationException();
}

@Override
protected boolean supports(@NotNull Class<?> clazz) {
return true;
}
}
;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import org.jetbrains.annotations.NotNull;
import org.springframework.http.InvalidMediaTypeException;
import org.jetbrains.annotations.Nullable;
import org.springframework.http.HttpMessage;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.EntityExchangeResult;

Expand All @@ -15,14 +16,20 @@ static String getContentAsString(EntityExchangeResult<byte[]> result) {

@NotNull
private static Charset getCharset(EntityExchangeResult<byte[]> result) {
try {
MediaType contentType = result.getResponseHeaders().getContentType();
if (contentType != null && contentType.getCharset() != null) {
return contentType.getCharset();
}
} catch (InvalidMediaTypeException e) {
// ignore
return getCharset(result.getResponseHeaders().getContentType());
}

@NotNull
static Charset getCharset(HttpMessage message) {
return getCharset(message.getHeaders().getContentType());
}

@NotNull
private static Charset getCharset(@Nullable MediaType contentType) {
if (contentType != null && contentType.getCharset() != null) {
return contentType.getCharset();
} else {
return StandardCharsets.UTF_8;
}
return StandardCharsets.UTF_8;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package net.javacrumbs.jsonunit.spring;

import static net.javacrumbs.jsonunit.spring.JsonUnitJsonComparator.comparator;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;

import net.javacrumbs.jsonunit.core.Configuration;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import org.springframework.test.json.JsonComparator;
import org.springframework.test.json.JsonContent;
import org.springframework.test.json.JsonContentAssert;

class JsonUnitJsonComparatorTest {

@Test
void shouldWorkWithSpringJsonContentAssert() {
new JsonContentAssert(json("{\"test\" : 1}")).isEqualTo("{test: 1}", comparator());
}

@Test
void shouldFailWithMessage() {
assertThatThrownBy(() -> new JsonContentAssert(json("{\"test\" : 1}")).isEqualTo("{test: 2}", comparator()))
.hasMessageEndingWith(
"""
JSON documents are different:
Different value found in node "test", expected: <2> but was: <1>.
""");
}

@Test
void shouldApplyConfiguration() {
JsonComparator comparator = comparator(Configuration.empty().withTolerance(0.01));
new JsonContentAssert(json("{\"test\" : 1.0001}")).isEqualTo("{test: 1}", comparator);
}

private static @NotNull JsonContent json(String json) {
return new JsonContent(json);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package net.javacrumbs.jsonunit.spring.testit;

import static java.util.Collections.singleton;
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.jsonUnitAssert;
import static net.javacrumbs.jsonunit.spring.JsonUnitJsonComparator.comparator;
import static net.javacrumbs.jsonunit.spring.JsonUnitJsonComparator.jsonUnitMessageConverter;
import static net.javacrumbs.jsonunit.spring.testit.demo.ExampleController.CORRECT_JSON;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;

import net.javacrumbs.jsonunit.core.Configuration;
import net.javacrumbs.jsonunit.spring.testit.demo.ExampleController;
import org.junit.jupiter.api.Test;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.test.web.servlet.assertj.MockMvcTester;

public class AssertJMockMvcTest {

@Test
void shouldWorkWithMockMvcTester() {
MockMvcTester mvc = MockMvcTester.of(new ExampleController());
assertThat(mvc.get().uri("/sample"))
.hasStatusOk()
.bodyJson()
.isEqualTo(CORRECT_JSON, comparator(Configuration.empty()));
}

@Test
void shouldUseConvertToDirect() {
MockMvcTester mvc = MockMvcTester.of(new ExampleController())
.withHttpMessageConverters(singleton(jsonUnitMessageConverter()));
assertThat(mvc.get().uri("/sample"))
.hasStatusOk()
.bodyJson()
.convertTo(jsonUnitAssert())
.inPath("result.array")
.isArray()
.containsExactly(1, 2, 3);
}

@Test
void shouldUseConvertToJAckson() {
MockMvcTester mvc = MockMvcTester.of(new ExampleController())
.withHttpMessageConverters(singleton(new MappingJackson2HttpMessageConverter()));
assertThat(mvc.get().uri("/sample"))
.hasStatusOk()
.bodyJson()
.convertTo(jsonUnitAssert())
.inPath("result.array")
.isArray()
.containsExactly(1, 2, 3);
}
}