diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/AbstractJsonMarshalTester.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/AbstractJsonMarshalTester.java index 29f4915ffc..1e09fdc7b0 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/AbstractJsonMarshalTester.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/AbstractJsonMarshalTester.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -129,7 +129,18 @@ public abstract class AbstractJsonMarshalTester { verify(); Assert.notNull(value, "Value must not be null"); String json = writeObject(value, this.type); - return new JsonContent<>(this.resourceLoadClass, this.type, json); + return getJsonContent(json); + } + + /** + * Factory method used to get a {@link JsonContent} instance from a source JSON + * string. + * @param json the source JSON + * @return a new {@link JsonContent} instance + * @since 2.1.5 + */ + protected JsonContent getJsonContent(String json) { + return new JsonContent<>(getResourceLoadClass(), getType(), json); } /** diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/BasicJsonTester.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/BasicJsonTester.java index e602e40196..ae593fbb83 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/BasicJsonTester.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/BasicJsonTester.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/GsonTester.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/GsonTester.java index 0f82f2fa1a..bcef31b0fb 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/GsonTester.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/GsonTester.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2019 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. diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/JacksonTester.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/JacksonTester.java index afe00a22f1..a971bc8a97 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/JacksonTester.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/JacksonTester.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2019 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. @@ -24,6 +24,9 @@ import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.ObjectWriter; +import com.jayway.jsonpath.Configuration; +import com.jayway.jsonpath.spi.json.JacksonJsonProvider; +import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider; import org.springframework.beans.factory.ObjectFactory; import org.springframework.core.ResolvableType; @@ -56,6 +59,7 @@ import org.springframework.util.Assert; * @param the type under test * @author Phillip Webb * @author Madhura Bhave + * @author Diego Berrueta * @since 1.4.0 */ public class JacksonTester extends AbstractJsonMarshalTester { @@ -92,6 +96,14 @@ public class JacksonTester extends AbstractJsonMarshalTester { this.view = view; } + @Override + protected JsonContent getJsonContent(String json) { + Configuration configuration = Configuration.builder() + .jsonProvider(new JacksonJsonProvider(this.objectMapper)) + .mappingProvider(new JacksonMappingProvider(this.objectMapper)).build(); + return new JsonContent<>(getResourceLoadClass(), getType(), json, configuration); + } + @Override protected T readObject(InputStream inputStream, ResolvableType type) throws IOException { diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonContent.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonContent.java index 87eb7be294..e2b1baab77 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonContent.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonContent.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -16,6 +16,7 @@ package org.springframework.boot.test.json; +import com.jayway.jsonpath.Configuration; import org.assertj.core.api.AssertProvider; import org.springframework.core.ResolvableType; @@ -28,6 +29,7 @@ import org.springframework.util.Assert; * * @param the source type that created the content * @author Phillip Webb + * @author Diego Berrueta * @since 1.4.0 */ public final class JsonContent implements AssertProvider { @@ -38,6 +40,8 @@ public final class JsonContent implements AssertProvider { private final String json; + private final Configuration configuration; + /** * Create a new {@link JsonContent} instance. * @param resourceLoadClass the source class used to load resources @@ -45,11 +49,25 @@ public final class JsonContent implements AssertProvider { * @param json the actual JSON content */ public JsonContent(Class resourceLoadClass, ResolvableType type, String json) { + this(resourceLoadClass, type, json, Configuration.defaultConfiguration()); + } + + /** + * Create a new {@link JsonContent} instance. + * @param resourceLoadClass the source class used to load resources + * @param type the type under test (or {@code null} if not known) + * @param json the actual JSON content + * @param configuration the JsonPath configuration + */ + JsonContent(Class resourceLoadClass, ResolvableType type, String json, + Configuration configuration) { Assert.notNull(resourceLoadClass, "ResourceLoadClass must not be null"); Assert.notNull(json, "JSON must not be null"); + Assert.notNull(configuration, "Configuration must not be null"); this.resourceLoadClass = resourceLoadClass; this.type = type; this.json = json; + this.configuration = configuration; } /** @@ -61,7 +79,8 @@ public final class JsonContent implements AssertProvider { @Override @Deprecated public JsonContentAssert assertThat() { - return new JsonContentAssert(this.resourceLoadClass, this.json); + return new JsonContentAssert(this.resourceLoadClass, null, this.json, + this.configuration); } /** diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonContentAssert.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonContentAssert.java index 87df230ed7..9eb44e896c 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonContentAssert.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonContentAssert.java @@ -22,6 +22,7 @@ import java.nio.charset.Charset; import java.util.List; import java.util.Map; +import com.jayway.jsonpath.Configuration; import com.jayway.jsonpath.JsonPath; import org.assertj.core.api.AbstractAssert; import org.assertj.core.api.AbstractBooleanAssert; @@ -45,12 +46,15 @@ import org.springframework.util.StringUtils; * * @author Phillip Webb * @author Andy Wilkinson + * @author Diego Berrueta * @since 1.4.0 */ public class JsonContentAssert extends AbstractAssert { private final JsonLoader loader; + private final Configuration configuration; + /** * Create a new {@link JsonContentAssert} instance that will load resources as UTF-8. * @param resourceLoadClass the source class used to load resources @@ -70,7 +74,21 @@ public class JsonContentAssert extends AbstractAssert resourceLoadClass, Charset charset, CharSequence json) { + this(resourceLoadClass, charset, json, Configuration.defaultConfiguration()); + } + + /** + * Create a new {@link JsonContentAssert} instance that will load resources in the + * given {@code charset}. + * @param resourceLoadClass the source class used to load resources + * @param charset the charset of the JSON resources + * @param json the actual JSON content + * @param configuration the json-path configuration + */ + JsonContentAssert(Class resourceLoadClass, Charset charset, CharSequence json, + Configuration configuration) { super(json, JsonContentAssert.class); + this.configuration = configuration; this.loader = new JsonLoader(resourceLoadClass, charset); } @@ -1109,7 +1127,8 @@ public class JsonContentAssert extends AbstractAssert simpleJson; + + private GsonTester> listJson; + + private GsonTester> mapJson; + + private GsonTester stringJson; + + private Gson gson; + + private static final String JSON = "{\"name\":\"Spring\",\"age\":123}"; + + @Before + public void setup() { + this.gson = new Gson(); + GsonTester.initFields(this, this.gson); + } + + @Test + public void typicalTest() throws Exception { + String example = JSON; + assertThat(this.simpleJson.parse(example).getObject().getName()) + .isEqualTo("Spring"); + } + + @Test + public void typicalListTest() throws Exception { + String example = "[" + JSON + "]"; + assertThat(this.listJson.parse(example)).asList().hasSize(1); + assertThat(this.listJson.parse(example).getObject().get(0).getName()) + .isEqualTo("Spring"); + } + + @Test + public void typicalMapTest() throws Exception { + Map map = new LinkedHashMap<>(); + map.put("a", 1); + map.put("b", 2); + assertThat(this.mapJson.write(map)).extractingJsonPathNumberValue("@.a") + .isEqualTo(1); + } + + @Test + public void stringLiteral() throws Exception { + String stringWithSpecialCharacters = "myString"; + assertThat(this.stringJson.write(stringWithSpecialCharacters)) + .extractingJsonPathStringValue("@") + .isEqualTo(stringWithSpecialCharacters); + } + +} diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/json/JacksonTesterIntegrationTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/json/JacksonTesterIntegrationTests.java index 8342bb2aa0..ab603230bf 100644 --- a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/json/JacksonTesterIntegrationTests.java +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/json/JacksonTesterIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2019 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. @@ -36,6 +36,7 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Phillip Webb * @author Madhura Bhave + * @author Diego Berrueta */ public class JacksonTesterIntegrationTests { @@ -47,6 +48,8 @@ public class JacksonTesterIntegrationTests { private JacksonTester> mapJson; + private JacksonTester stringJson; + private ObjectMapper objectMapper; private static final String JSON = "{\"name\":\"Spring\",\"age\":123}"; @@ -81,6 +84,28 @@ public class JacksonTesterIntegrationTests { .isEqualTo(1); } + @Test + public void stringLiteral() throws Exception { + String stringWithSpecialCharacters = "myString"; + assertThat(this.stringJson.write(stringWithSpecialCharacters)) + .extractingJsonPathStringValue("@") + .isEqualTo(stringWithSpecialCharacters); + } + + @Test + public void parseSpecialCharactersTest() throws Exception { + // Confirms that the handling of special characters is symmetrical between + // the serialization (via the JacksonTester) and the parsing (via json-path). By + // default json-path uses SimpleJson as its parser, which has a slightly different + // behavior to Jackson and breaks the symmetry. JacksonTester + // configures json-path to use Jackson for evaluating the path expressions and + // restores the symmetry. See gh-15727 + String stringWithSpecialCharacters = "\u0006\u007F"; + assertThat(this.stringJson.write(stringWithSpecialCharacters)) + .extractingJsonPathStringValue("@") + .isEqualTo(stringWithSpecialCharacters); + } + @Test public void writeWithView() throws Exception { this.objectMapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION); diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/json/JsonContentTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/json/JsonContentTests.java index bf0859f216..a27f83d8b8 100644 --- a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/json/JsonContentTests.java +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/json/JsonContentTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.test.json; +import com.jayway.jsonpath.Configuration; import org.junit.Test; import org.springframework.core.ResolvableType; @@ -38,47 +39,61 @@ public class JsonContentTests { @Test public void createWhenResourceLoadClassIsNullShouldThrowException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new JsonContent(null, TYPE, JSON)) + .isThrownBy(() -> new JsonContent(null, TYPE, JSON, + Configuration.defaultConfiguration())) .withMessageContaining("ResourceLoadClass must not be null"); } @Test public void createWhenJsonIsNullShouldThrowException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new JsonContent(getClass(), TYPE, null)) + .isThrownBy(() -> new JsonContent(getClass(), TYPE, null, + Configuration.defaultConfiguration())) .withMessageContaining("JSON must not be null"); } + @Test + public void createWhenConfigurationIsNullShouldThrowException() { + assertThatIllegalArgumentException().isThrownBy( + () -> new JsonContent(getClass(), TYPE, JSON, null)) + .withMessageContaining("Configuration must not be null"); + } + @Test public void createWhenTypeIsNullShouldCreateContent() { - JsonContent content = new JsonContent<>(getClass(), null, JSON); + JsonContent content = new JsonContent<>(getClass(), null, JSON, + Configuration.defaultConfiguration()); assertThat(content).isNotNull(); } @Test @SuppressWarnings("deprecation") public void assertThatShouldReturnJsonContentAssert() { - JsonContent content = new JsonContent<>(getClass(), TYPE, JSON); + JsonContent content = new JsonContent<>(getClass(), TYPE, JSON, + Configuration.defaultConfiguration()); assertThat(content.assertThat()).isInstanceOf(JsonContentAssert.class); } @Test public void getJsonShouldReturnJson() { - JsonContent content = new JsonContent<>(getClass(), TYPE, JSON); + JsonContent content = new JsonContent<>(getClass(), TYPE, JSON, + Configuration.defaultConfiguration()); assertThat(content.getJson()).isEqualTo(JSON); } @Test public void toStringWhenHasTypeShouldReturnString() { - JsonContent content = new JsonContent<>(getClass(), TYPE, JSON); + JsonContent content = new JsonContent<>(getClass(), TYPE, JSON, + Configuration.defaultConfiguration()); assertThat(content.toString()) .isEqualTo("JsonContent " + JSON + " created from " + TYPE); } @Test public void toStringWhenHasNoTypeShouldReturnString() { - JsonContent content = new JsonContent<>(getClass(), null, JSON); + JsonContent content = new JsonContent<>(getClass(), null, JSON, + Configuration.defaultConfiguration()); assertThat(content.toString()).isEqualTo("JsonContent " + JSON); }