From 24ab2bd8918c115be329c7fbbff5f819e6e8a1e5 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 22 Mar 2016 10:46:07 -0700 Subject: [PATCH] Add utilities to help with JSON testing Add Jackson, Gson and Basic String helper classes that allow AssertJ assertions to be used to test JSON. Fixes gh-5471 --- spring-boot-test/pom.xml | 19 + .../test/json/AbstractJsonMarshalTester.java | 372 +++++ .../boot/test/json/BasicJsonTester.java | 128 ++ .../boot/test/json/GsonTester.java | 119 ++ .../boot/test/json/JacksonTester.java | 136 ++ .../boot/test/json/JsonContent.java | 74 + .../boot/test/json/JsonContentAssert.java | 1173 +++++++++++++++ .../boot/test/json/JsonLoader.java | 78 + .../boot/test/json/ObjectContent.java | 69 + .../boot/test/json/ObjectContentAssert.java | 63 + .../boot/test/json/package-info.java | 20 + .../json/AbstractJsonMarshalTesterTests.java | 212 +++ .../boot/test/json/BasicJsonTesterTests.java | 97 ++ .../boot/test/json/ExampleObject.java | 66 + .../boot/test/json/GsonTesterTests.java | 89 ++ .../json/JacksonTesterIntegrationTests.java | 62 + .../boot/test/json/JacksonTesterTests.java | 87 ++ .../test/json/JsonContentAssertTests.java | 1339 +++++++++++++++++ .../boot/test/json/JsonContentTests.java | 93 ++ .../test/json/ObjectContentAssertTests.java | 86 ++ .../boot/test/json/ObjectContentTests.java | 85 ++ .../boot/test/json/different.json | 6 + .../boot/test/json/example.json | 4 + .../boot/test/json/lenient-same.json | 6 + .../boot/test/json/simpsons.json | 19 + .../boot/test/json/source.json | 6 + .../springframework/boot/test/json/types.json | 19 + 27 files changed, 4527 insertions(+) create mode 100644 spring-boot-test/src/main/java/org/springframework/boot/test/json/AbstractJsonMarshalTester.java create mode 100644 spring-boot-test/src/main/java/org/springframework/boot/test/json/BasicJsonTester.java create mode 100644 spring-boot-test/src/main/java/org/springframework/boot/test/json/GsonTester.java create mode 100644 spring-boot-test/src/main/java/org/springframework/boot/test/json/JacksonTester.java create mode 100644 spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonContent.java create mode 100644 spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonContentAssert.java create mode 100644 spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonLoader.java create mode 100644 spring-boot-test/src/main/java/org/springframework/boot/test/json/ObjectContent.java create mode 100644 spring-boot-test/src/main/java/org/springframework/boot/test/json/ObjectContentAssert.java create mode 100644 spring-boot-test/src/main/java/org/springframework/boot/test/json/package-info.java create mode 100644 spring-boot-test/src/test/java/org/springframework/boot/test/json/AbstractJsonMarshalTesterTests.java create mode 100644 spring-boot-test/src/test/java/org/springframework/boot/test/json/BasicJsonTesterTests.java create mode 100644 spring-boot-test/src/test/java/org/springframework/boot/test/json/ExampleObject.java create mode 100644 spring-boot-test/src/test/java/org/springframework/boot/test/json/GsonTesterTests.java create mode 100644 spring-boot-test/src/test/java/org/springframework/boot/test/json/JacksonTesterIntegrationTests.java create mode 100644 spring-boot-test/src/test/java/org/springframework/boot/test/json/JacksonTesterTests.java create mode 100644 spring-boot-test/src/test/java/org/springframework/boot/test/json/JsonContentAssertTests.java create mode 100644 spring-boot-test/src/test/java/org/springframework/boot/test/json/JsonContentTests.java create mode 100644 spring-boot-test/src/test/java/org/springframework/boot/test/json/ObjectContentAssertTests.java create mode 100644 spring-boot-test/src/test/java/org/springframework/boot/test/json/ObjectContentTests.java create mode 100644 spring-boot-test/src/test/resources/org/springframework/boot/test/json/different.json create mode 100644 spring-boot-test/src/test/resources/org/springframework/boot/test/json/example.json create mode 100644 spring-boot-test/src/test/resources/org/springframework/boot/test/json/lenient-same.json create mode 100644 spring-boot-test/src/test/resources/org/springframework/boot/test/json/simpsons.json create mode 100644 spring-boot-test/src/test/resources/org/springframework/boot/test/json/source.json create mode 100644 spring-boot-test/src/test/resources/org/springframework/boot/test/json/types.json diff --git a/spring-boot-test/pom.xml b/spring-boot-test/pom.xml index e09540488c..a8331732a4 100644 --- a/spring-boot-test/pom.xml +++ b/spring-boot-test/pom.xml @@ -25,6 +25,16 @@ spring-boot + + com.fasterxml.jackson.core + jackson-databind + true + + + com.google.code.gson + gson + true + javax.servlet javax.servlet-api @@ -60,6 +70,11 @@ mockito-core true + + org.skyscreamer + jsonassert + true + org.springframework spring-test @@ -102,6 +117,10 @@ true test + + com.jayway.jsonpath + json-path + diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/json/AbstractJsonMarshalTester.java b/spring-boot-test/src/main/java/org/springframework/boot/test/json/AbstractJsonMarshalTester.java new file mode 100644 index 0000000000..43e2d673fb --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/json/AbstractJsonMarshalTester.java @@ -0,0 +1,372 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.boot.test.json; + +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; +import java.lang.reflect.Field; + +import org.assertj.core.api.Assertions; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.core.ResolvableType; +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.util.Assert; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.ReflectionUtils.FieldCallback; + +/** + * Base class for AssertJ based JSON marshal testers. Exposes specific Asserts following a + * {@code read}, {@code write} or {@code parse} of JSON content. Typically used in + * combination with an AssertJ {@link Assertions#assertThat(Object) assertThat} call. For + * example:
+ * public class ExampleObjectJsonTests {
+ *
+ *     private AbstractJsonTester<ExampleObject> json = //...
+ *
+ *     @Test
+ *     public void testWriteJson() {
+ *         ExampleObject object = //...
+ *         assertThat(json.write(object)).isEqualToJson("expected.json");
+ *         assertThat(json.read("expected.json)).isEqualTo(object);
+ *     }
+ *
+ * }
+ * 
For a complete list of supported assertions see {@link JsonContentAssert} and + * {@link ObjectContentAssert}. + *

+ * To use this library JSONAssert must be on the test classpath. + * + * @param The type under test + * @author Phillip Webb + * @since 1.4.0 + * @see JsonContentAssert + * @see ObjectContentAssert + */ +public abstract class AbstractJsonMarshalTester { + + private final Class resourceLoadClass; + + private final ResolvableType type; + + /** + * Create a new {@link AbstractJsonMarshalTester} instance. + * @param resourceLoadClass the source class used when loading relative classpath + * resources + * @param type the type under test + */ + public AbstractJsonMarshalTester(Class resourceLoadClass, ResolvableType type) { + Assert.notNull(resourceLoadClass, "ResourceLoadClass must not be null"); + Assert.notNull(type, "Type must not be null"); + this.resourceLoadClass = resourceLoadClass; + this.type = type; + } + + /** + * Return the type under test. + * @return the type under test + */ + protected final ResolvableType getType() { + return this.type; + } + + /** + * Return {@link JsonContent} from writing the specific value. + * @param value the value to write + * @return the {@link JsonContent} + * @throws IOException on write error + */ + public JsonContent write(T value) throws IOException { + Assert.notNull(value, "Value must not be null"); + String json = writeObject(value, this.type); + return new JsonContent(this.resourceLoadClass, this.type, json); + } + + /** + * Return the object created from parsing the specific JSON bytes. + * @param jsonBytes the source JSON bytes + * @return the resulting object + * @throws IOException on parse error + */ + public T parseObject(byte[] jsonBytes) throws IOException { + return parse(jsonBytes).getObject(); + } + + /** + * Return {@link ObjectContent} from parsing the specific JSON bytes. + * @param jsonBytes the source JSON bytes + * @return the {@link ObjectContent} + * @throws IOException on parse error + */ + public ObjectContent parse(byte[] jsonBytes) throws IOException { + Assert.notNull(jsonBytes, "JsonBytes must not be null"); + return read(new ByteArrayResource(jsonBytes)); + } + + /** + * Return the object created from parsing the specific JSON String. + * @param jsonString the source JSON string + * @return the resulting object + * @throws IOException on parse error + */ + public T parseObject(String jsonString) throws IOException { + return parse(jsonString).getObject(); + } + + /** + * Return {@link ObjectContent} from parsing the specific JSON String. + * @param jsonString the source JSON string + * @return the {@link ObjectContent} + * @throws IOException on parse error + */ + public ObjectContent parse(String jsonString) throws IOException { + Assert.notNull(jsonString, "JsonString must not be null"); + return read(new StringReader(jsonString)); + } + + /** + * Return the object created from reading from the specified classpath resource. + * @param resourcePath the source resource path. May be a full path or a path relative + * to the {@code resourceLoadClass} passed to the constructor + * @return the resulting object + * @throws IOException on read error + */ + public T readObject(String resourcePath) throws IOException { + return read(resourcePath).getObject(); + } + + /** + * Return {@link ObjectContent} from reading from the specified classpath resource. + * @param resourcePath the source resource path. May be a full path or a path relative + * to the {@code resourceLoadClass} passed to the constructor + * @return the {@link ObjectContent} + * @throws IOException on read error + */ + public ObjectContent read(String resourcePath) throws IOException { + Assert.notNull(resourcePath, "ResourcePath must not be null"); + return read(new ClassPathResource(resourcePath, this.resourceLoadClass)); + } + + /** + * Return the object created from reading from the specified file. + * @param file the source file + * @return the resulting object + * @throws IOException on read error + */ + public T readObject(File file) throws IOException { + return read(file).getObject(); + } + + /** + * Return {@link ObjectContent} from reading from the specified file. + * @param file the source file + * @return the {@link ObjectContent} + * @throws IOException on read error + */ + public ObjectContent read(File file) throws IOException { + Assert.notNull(file, "File must not be null"); + return read(new FileSystemResource(file)); + } + + /** + * Return the object created from reading from the specified input stream. + * @param inputStream the source input stream + * @return the resulting object + * @throws IOException on read error + */ + public T readObject(InputStream inputStream) throws IOException { + return read(inputStream).getObject(); + } + + /** + * Return {@link ObjectContent} from reading from the specified input stream. + * @param inputStream the source input stream + * @return the {@link ObjectContent} + * @throws IOException on read error + */ + public ObjectContent read(InputStream inputStream) throws IOException { + Assert.notNull(inputStream, "InputStream must not be null"); + return read(new InputStreamResource(inputStream)); + } + + /** + * Return the object created from reading from the specified resource. + * @param resource the source resource + * @return the resulting object + * @throws IOException on read error + */ + public T readObject(Resource resource) throws IOException { + return read(resource).getObject(); + } + + /** + * Return {@link ObjectContent} from reading from the specified resource. + * @param resource the source resource + * @return the {@link ObjectContent} + * @throws IOException on read error + */ + public ObjectContent read(Resource resource) throws IOException { + Assert.notNull(resource, "Resource must not be null"); + InputStream inputStream = resource.getInputStream(); + T object = readObject(inputStream, this.type); + closeQuietly(inputStream); + return new ObjectContent(this.type, object); + } + + /** + * Return the object created from reading from the specified reader. + * @param reader the source reader + * @return the resulting object + * @throws IOException on read error + */ + public T readObject(Reader reader) throws IOException { + return read(reader).getObject(); + } + + /** + * Return {@link ObjectContent} from reading from the specified reader. + * @param reader the source reader + * @return the {@link ObjectContent} + * @throws IOException on read error + */ + public ObjectContent read(Reader reader) throws IOException { + Assert.notNull(reader, "Reader must not be null"); + T object = readObject(reader, this.type); + closeQuietly(reader); + return new ObjectContent(this.type, object); + } + + private void closeQuietly(Closeable closeable) { + try { + closeable.close(); + } + catch (IOException ex) { + } + } + + /** + * Write the specified object to a JSON string. + * @param value the source value ({@code never null}) + * @param type the resulting type ({@code never null}) + * @return the JSON string + * @throws IOException on write error + */ + protected abstract String writeObject(T value, ResolvableType type) + throws IOException; + + /** + * Read from the specified input stream to create an object of the specified type. The + * default implementation delegates to {@link #readObject(Reader, ResolvableType)} + * @param inputStream the source input stream ({@code never null}) + * @param type the resulting type ({@code never null}) + * @return the resulting object + * @throws IOException on read error + */ + protected T readObject(InputStream inputStream, ResolvableType type) + throws IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + return readObject(reader, type); + } + + /** + * Read from the specified reader to create an object of the specified type. + * @param reader the source reader ({@code never null}) + * @param type the resulting type ({@code never null}) + * @return the resulting object + * @throws IOException on read error + */ + protected abstract T readObject(Reader reader, ResolvableType type) + throws IOException; + + /** + * Utility class used to support field initialization. Uses by subclasses to support + * {@code initFields}. + * @param The marshaller type + */ + protected static abstract class FieldInitializer { + + private final Class testerClass; + + @SuppressWarnings("rawtypes") + protected FieldInitializer( + Class testerClass) { + Assert.notNull(testerClass, "TesterClass must not be null"); + this.testerClass = testerClass; + } + + public void initFields(final Object testInstance, final M marshaller) { + Assert.notNull(testInstance, "TestInstance must not be null"); + Assert.notNull(marshaller, "Marshaller must not be null"); + initFields(testInstance, new ObjectFactory() { + + @Override + public M getObject() throws BeansException { + return marshaller; + } + + }); + } + + public void initFields(final Object testInstance, + final ObjectFactory marshaller) { + Assert.notNull(testInstance, "TestInstance must not be null"); + Assert.notNull(marshaller, "Marshaller must not be null"); + ReflectionUtils.doWithFields(testInstance.getClass(), new FieldCallback() { + + @Override + public void doWith(Field field) + throws IllegalArgumentException, IllegalAccessException { + doWithField(field, testInstance, marshaller); + } + + }); + } + + protected void doWithField(Field field, Object test, + ObjectFactory marshaller) { + if (this.testerClass.isAssignableFrom(field.getType())) { + ReflectionUtils.makeAccessible(field); + Object existingValue = ReflectionUtils.getField(field, test); + if (existingValue == null) { + setupField(field, test, marshaller); + } + } + } + + private void setupField(Field field, Object test, ObjectFactory marshaller) { + ResolvableType type = ResolvableType.forField(field).getGeneric(); + ReflectionUtils.setField(field, test, + createTester(test.getClass(), type, marshaller.getObject())); + } + + protected abstract AbstractJsonMarshalTester createTester( + Class resourceLoadClass, ResolvableType type, M marshaller); + + } + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/json/BasicJsonTester.java b/spring-boot-test/src/main/java/org/springframework/boot/test/json/BasicJsonTester.java new file mode 100644 index 0000000000..9ce37b8883 --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/json/BasicJsonTester.java @@ -0,0 +1,128 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.boot.test.json; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import org.springframework.core.io.Resource; +import org.springframework.util.Assert; + +/** + * AssertJ based JSON tester that works with basic JSON strings. Allows testing of JSON + * payloads created from some any source, for example:
+ * public class ExampleObjectJsonTests {
+ *
+ *     private BasicJsonTester = new BasicJsonTester(getClass());
+ *
+ *     @Test
+ *     public void testWriteJson() {
+ *         assertThat(json.from("example.json")).extractingJsonPathStringValue("@.name")
+				.isEqualTo("Spring");
+ *     }
+ *
+ * }
+ * 
+ * + * See {@link AbstractJsonMarshalTester} for more details. + * + * @author Phillip Webb + * @since 1.4.0 + */ +public class BasicJsonTester { + + private final JsonLoader loader; + + /** + * Create a new {@link BasicJsonTester} instance. + * @param resourceLoadClass the source class used to load resources + */ + public BasicJsonTester(Class resourceLoadClass) { + Assert.notNull(resourceLoadClass, "ResourceLoadClass must not be null"); + this.loader = new JsonLoader(resourceLoadClass); + } + + /** + * Create JSON content from the specified String source. The source can contain the + * JSON itself or, if it ends with {@code .json}, the name of a resource to be loaded + * using {@code resourceLoadClass}. + * @param source JSON content or a {@code .json} resource name + * @return the JSON content + * @throws IOException on load error + */ + public JsonContent from(CharSequence source) throws IOException { + return getJsonContent(this.loader.getJson(source)); + } + + /** + * Create JSON content from the specified resource path. + * @param path the path of the resource to load + * @param resourceLoadClass the classloader used load the resource + * @return the JSON content + * @throws IOException on load error + */ + public JsonContent from(String path, Class resourceLoadClass) + throws IOException { + return getJsonContent(this.loader.getJson(path, resourceLoadClass)); + } + + /** + * Create JSON content from the specified JSON bytes. + * @param source the bytes of JSON + * @return the JSON content + * @throws IOException on load error + */ + public JsonContent from(byte[] source) throws IOException { + return getJsonContent(this.loader.getJson(source)); + } + + /** + * Create JSON content from the specified JSON file. + * @param source the file containing JSON + * @return the JSON content + * @throws IOException on load error + */ + public JsonContent from(File source) throws IOException { + return getJsonContent(this.loader.getJson(source)); + } + + /** + * Create JSON content from the specified JSON input stream. + * @param source the input stream containing JSON + * @return the JSON content + * @throws IOException on load error + */ + public JsonContent from(InputStream source) throws IOException { + return getJsonContent(this.loader.getJson(source)); + } + + /** + * Create JSON content from the specified JSON resource. + * @param source the resource containing JSON + * @return the JSON content + * @throws IOException on load error + */ + public JsonContent from(Resource source) throws IOException { + return getJsonContent(this.loader.getJson(source)); + } + + private JsonContent getJsonContent(String json) { + return new JsonContent(this.loader.getResourceLoadClass(), null, json); + } + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/json/GsonTester.java b/spring-boot-test/src/main/java/org/springframework/boot/test/json/GsonTester.java new file mode 100644 index 0000000000..a27da3723e --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/json/GsonTester.java @@ -0,0 +1,119 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.boot.test.json; + +import java.io.IOException; +import java.io.Reader; + +import com.google.gson.Gson; + +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.core.ResolvableType; +import org.springframework.util.Assert; + +/** + * AssertJ based JSON tester backed by Gson. Usually instantiated via + * {@link #initFields(Object, Gson)}, for example:
+ * public class ExampleObjectJsonTests {
+ *
+ *     private GsonTester<ExampleObject> json;
+ *
+ *     @Before
+ *     public void setup() {
+ *         Gson gson = new GsonBuilder().create();
+ *         GsonTester.initFields(this, gson);
+ *     }
+ *
+ *     @Test
+ *     public void testWriteJson() {
+ *         ExampleObject object = //...
+ *         assertThat(json.write(object)).isEqualToJson("expected.json");
+ *     }
+ *
+ * }
+ * 
+ * + * See {@link AbstractJsonMarshalTester} for more details. + * + * @param The type under test + * @author Phillip Webb + * @since 1.4.0 + */ +public class GsonTester extends AbstractJsonMarshalTester { + + private final Gson gson; + + /** + * Create a new {@link GsonTester} instance. + * @param resourceLoadClass the source class used to load resources + * @param type the type under test + * @param gson the Gson instance + * @see #initFields(Object, Gson) + */ + public GsonTester(Class resourceLoadClass, ResolvableType type, Gson gson) { + super(resourceLoadClass, type); + Assert.notNull(gson, "Gson must not be null"); + this.gson = gson; + } + + @Override + protected String writeObject(T value, ResolvableType type) throws IOException { + return this.gson.toJson(value, type.getType()); + } + + @Override + protected T readObject(Reader reader, ResolvableType type) throws IOException { + return this.gson.fromJson(reader, type.getType()); + } + + /** + * Utility method to initialize {@link JacksonTester} fields. See {@link JacksonTester + * class-level documentation} for example usage. + * @param testInstance the test instance + * @param gson the Gson instance + */ + public static void initFields(Object testInstance, Gson gson) { + new GsonFieldInitializer().initFields(testInstance, gson); + } + + /** + * Utility method to initialize {@link JacksonTester} fields. See {@link JacksonTester + * class-level documentation} for example usage. + * @param testInstance the test instance + * @param gson an object factory to create the Gson instance + */ + public static void initFields(Object testInstance, ObjectFactory gson) { + new GsonFieldInitializer().initFields(testInstance, gson); + } + + /** + * {@link JsonTesterFieldInitializer} for Gson. + */ + private static class GsonFieldInitializer extends FieldInitializer { + + protected GsonFieldInitializer() { + super(GsonTester.class); + } + + @Override + protected AbstractJsonMarshalTester createTester( + Class resourceLoadClass, ResolvableType type, Gson marshaller) { + return new GsonTester(resourceLoadClass, type, marshaller); + } + + } +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/json/JacksonTester.java b/spring-boot-test/src/main/java/org/springframework/boot/test/json/JacksonTester.java new file mode 100644 index 0000000000..b0eec58df0 --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/json/JacksonTester.java @@ -0,0 +1,136 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.boot.test.json; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.core.ResolvableType; +import org.springframework.util.Assert; + +/** + * AssertJ based JSON tester backed by Jackson. Usually instantiated via + * {@link #initFields(Object, ObjectMapper)}, for example:
+ * public class ExampleObjectJsonTests {
+ *
+ *     private JacksonTester<ExampleObject> json;
+ *
+ *     @Before
+ *     public void setup() {
+ *         ObjectMapper objectMapper = new ObjectMapper();
+ *         JacksonTester.initFields(this, objectMapper);
+ *     }
+ *
+ *     @Test
+ *     public void testWriteJson() {
+ *         ExampleObject object = //...
+ *         assertThat(json.write(object)).isEqualToJson("expected.json");
+ *     }
+ *
+ * }
+ * 
+ * + * See {@link AbstractJsonMarshalTester} for more details. + * + * @param The type under test + * @author Phillip Webb + * @since 1.4.0 + */ +public class JacksonTester extends AbstractJsonMarshalTester { + + private final ObjectMapper objectMapper; + + /** + * Create a new {@link JacksonTester} instance. + * @param resourceLoadClass the source class used to load resources + * @param type the type under test + * @param objectMapper the Jackson object mapper + */ + public JacksonTester(Class resourceLoadClass, ResolvableType type, + ObjectMapper objectMapper) { + super(resourceLoadClass, type); + Assert.notNull(objectMapper, "ObjectMapper must not be null"); + this.objectMapper = objectMapper; + } + + @Override + protected T readObject(InputStream inputStream, ResolvableType type) + throws IOException { + return this.objectMapper.readValue(inputStream, getType(type)); + } + + @Override + protected T readObject(Reader reader, ResolvableType type) throws IOException { + return this.objectMapper.readerFor(getType(type)).readValue(reader); + } + + @Override + protected String writeObject(T value, ResolvableType type) throws IOException { + return this.objectMapper.writerFor(getType(type)).writeValueAsString(value); + } + + private JavaType getType(ResolvableType type) { + return this.objectMapper.constructType(type.getType()); + } + + /** + * Utility method to initialize {@link JacksonTester} fields. See {@link JacksonTester + * class-level documentation} for example usage. + * @param testInstance the test instance + * @param objectMapper the object mapper + * @see #initFields(Object, ObjectMapper) + */ + public static void initFields(Object testInstance, ObjectMapper objectMapper) { + new JacksonFieldInitializer().initFields(testInstance, objectMapper); + } + + /** + * Utility method to initialize {@link JacksonTester} fields. See {@link JacksonTester + * class-level documentation} for example usage. + * @param testInstance the test instance + * @param objectMapperFactory a factory to create the object mapper + * @see #initFields(Object, ObjectMapper) + */ + public static void initFields(Object testInstance, + ObjectFactory objectMapperFactory) { + new JacksonFieldInitializer().initFields(testInstance, objectMapperFactory); + } + + /** + * {@link JsonTesterFieldInitializer} for Jackson. + */ + private static class JacksonFieldInitializer extends FieldInitializer { + + protected JacksonFieldInitializer() { + super(JacksonTester.class); + } + + @Override + protected AbstractJsonMarshalTester createTester( + Class resourceLoadClass, ResolvableType type, + ObjectMapper marshaller) { + return new JacksonTester(resourceLoadClass, type, marshaller); + } + + } + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonContent.java b/spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonContent.java new file mode 100644 index 0000000000..12469e1205 --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonContent.java @@ -0,0 +1,74 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.boot.test.json; + +import org.assertj.core.api.AssertProvider; + +import org.springframework.core.ResolvableType; +import org.springframework.util.Assert; + +/** + * JSON content created usually from a JSON tester. Generally used only to + * {@link AssertProvider provide} {@link JsonContentAssert} to AssertJ {@code assertThat} + * calls. + * + * @param The source type that created the content + * @author Phillip Webb + * @since 1.4.0 + */ +public final class JsonContent implements AssertProvider { + + private final Class resourceLoadClass; + + private final ResolvableType type; + + private final String json; + + /** + * 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 + */ + public JsonContent(Class resourceLoadClass, ResolvableType type, String json) { + Assert.notNull(resourceLoadClass, "ResourceLoadClass must not be null"); + Assert.notNull(json, "JSON must not be null"); + this.resourceLoadClass = resourceLoadClass; + this.type = type; + this.json = json; + } + + @Override + public JsonContentAssert assertThat() { + return new JsonContentAssert(this.resourceLoadClass, this.json); + } + + /** + * Return the actual JSON content string. + * @return the JSON content + */ + public String getJson() { + return this.json; + } + + @Override + public String toString() { + return "JsonContent " + this.json + + (this.type == null ? "" : " created from " + this.type); + } + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonContentAssert.java b/spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonContentAssert.java new file mode 100644 index 0000000000..5cd1e6c13f --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonContentAssert.java @@ -0,0 +1,1173 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.boot.test.json; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Map; + +import com.jayway.jsonpath.JsonPath; +import org.assertj.core.api.AbstractAssert; +import org.assertj.core.api.AbstractBooleanAssert; +import org.assertj.core.api.AbstractCharSequenceAssert; +import org.assertj.core.api.AbstractListAssert; +import org.assertj.core.api.AbstractMapAssert; +import org.assertj.core.api.AbstractObjectAssert; +import org.assertj.core.api.Assert; +import org.assertj.core.api.Assertions; +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.Resource; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; + +/** + * AssertJ {@link Assert} for {@link JsonContent}. + * + * @author Phillip Webb + * @since 1.4.0 + */ +public class JsonContentAssert extends AbstractAssert { + + private final JsonLoader loader; + + /** + * Create a new {@link JsonContentAssert} instance. + * @param resourceLoadClass the source class used to load resources + * @param json the actual JSON content + */ + public JsonContentAssert(Class resourceLoadClass, CharSequence json) { + super(json, JsonContentAssert.class); + this.loader = new JsonLoader(resourceLoadClass); + } + + /** + * Overridden version of {@code isEqualTo} to perform JSON tests based on the object + * type. + * @see org.assertj.core.api.AbstractAssert#isEqualTo(java.lang.Object) + */ + @Override + public JsonContentAssert isEqualTo(Object expected) { + try { + if (expected == null || expected instanceof CharSequence) { + return isEqualToJson((CharSequence) expected); + } + if (expected instanceof byte[]) { + return isEqualToJson((byte[]) expected); + } + if (expected instanceof File) { + return isEqualToJson((File) expected); + } + if (expected instanceof InputStream) { + return isEqualToJson((InputStream) expected); + } + if (expected instanceof Resource) { + return isEqualToJson((Resource) expected); + } + throw new AssertionError( + "Unsupport type for JSON assert " + expected.getClass()); + } + catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + + /** + * Verifies that the actual value is {@link JSONCompareMode#LENIENT leniently} equal + * to the specified 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 using + * {@code resourceLoadClass}. + * @param expected the expected JSON or the name of a resource containing the expected + * JSON + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isEqualToJson(CharSequence expected) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotFailed(compare(expectedJson, JSONCompareMode.LENIENT)); + } + + /** + * Verifies that the actual value is {@link JSONCompareMode#LENIENT leniently} equal + * to the specified JSON resource. + * @param path the name of a resource containing the expected JSON + * @param resourceLoadClass the source class used to load the resource + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isEqualToJson(String path, Class resourceLoadClass) + throws IOException { + String expectedJson = this.loader.getJson(path, resourceLoadClass); + return assertNotFailed(compare(expectedJson, JSONCompareMode.LENIENT)); + } + + /** + * Verifies that the actual value is {@link JSONCompareMode#LENIENT leniently} equal + * to the specified JSON bytes. + * @param expected the expected JSON bytes + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isEqualToJson(byte[] expected) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotFailed(compare(expectedJson, JSONCompareMode.LENIENT)); + } + + /** + * Verifies that the actual value is {@link JSONCompareMode#LENIENT leniently} equal + * to the specified JSON file. + * @param expected a file containing the expected JSON + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isEqualToJson(File expected) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotFailed(compare(expectedJson, JSONCompareMode.LENIENT)); + } + + /** + * Verifies that the actual value is {@link JSONCompareMode#LENIENT leniently} equal + * to the specified JSON input stream. + * @param expected an input stream containing the expected JSON + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isEqualToJson(InputStream expected) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotFailed(compare(expectedJson, JSONCompareMode.LENIENT)); + } + + /** + * Verifies that the actual value is {@link JSONCompareMode#LENIENT leniently} equal + * to the specified JSON resource. + * @param expected a resource containing the expected JSON + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isEqualToJson(Resource expected) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotFailed(compare(expectedJson, JSONCompareMode.LENIENT)); + } + + /** + * Verifies that the actual value is {@link JSONCompareMode#STRICT strictly} equal to + * the specified 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 using + * {@code resourceLoadClass}. + * @param expected the expected JSON or the name of a resource containing the expected + * JSON + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isStrictlyEqualToJson(CharSequence expected) + throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotFailed(compare(expectedJson, JSONCompareMode.STRICT)); + } + + /** + * Verifies that the actual value is {@link JSONCompareMode#STRICT strictly} equal to + * the specified JSON resource. + * @param path the name of a resource containing the expected JSON + * @param resourceLoadClass the source class used to load the resource + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isStrictlyEqualToJson(String path, + Class resourceLoadClass) throws IOException { + String expectedJson = this.loader.getJson(path, resourceLoadClass); + return assertNotFailed(compare(expectedJson, JSONCompareMode.STRICT)); + } + + /** + * Verifies that the actual value is {@link JSONCompareMode#STRICT strictly} equal to + * the specified JSON bytes. + * @param expected the expected JSON bytes + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isStrictlyEqualToJson(byte[] expected) throws IOException { + return assertNotFailed( + compare(this.loader.getJson(expected), JSONCompareMode.STRICT)); + } + + /** + * Verifies that the actual value is {@link JSONCompareMode#STRICT strictly} equal to + * the specified JSON file. + * @param expected a file containing the expected JSON + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isStrictlyEqualToJson(File expected) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotFailed(compare(expectedJson, JSONCompareMode.STRICT)); + } + + /** + * Verifies that the actual value is {@link JSONCompareMode#STRICT strictly} equal to + * the specified JSON input stream. + * @param expected an input stream containing the expected JSON + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isStrictlyEqualToJson(InputStream expected) + throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotFailed(compare(expectedJson, JSONCompareMode.STRICT)); + } + + /** + * Verifies that the actual value is {@link JSONCompareMode#STRICT strictly} equal to + * the specified JSON resource. + * @param expected a resource containing the expected JSON + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isStrictlyEqualToJson(Resource expected) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotFailed(compare(expectedJson, JSONCompareMode.STRICT)); + } + + /** + * Verifies that the actual value is equal to the specified 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 using {@code resourceLoadClass}. + * @param expected the expected JSON or the name of a resource containing the expected + * JSON + * @param compareMode the compare mode used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isEqualToJson(CharSequence expected, + JSONCompareMode compareMode) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotFailed(compare(expectedJson, compareMode)); + } + + /** + * Verifies that the actual value is equal to the specified JSON resource. + * @param path the name of a resource containing the expected JSON + * @param resourceLoadClass the source class used to load the resource + * @param compareMode the compare mode used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isEqualToJson(String path, Class resourceLoadClass, + JSONCompareMode compareMode) throws IOException { + String expectedJson = this.loader.getJson(path, resourceLoadClass); + return assertNotFailed(compare(expectedJson, compareMode)); + } + + /** + * Verifies that the actual value is equal to the specified JSON bytes. + * @param expected the expected JSON bytes + * @param compareMode the compare mode used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isEqualToJson(byte[] expected, JSONCompareMode compareMode) + throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotFailed(compare(expectedJson, compareMode)); + } + + /** + * Verifies that the actual value is equal to the specified JSON file. + * @param expected a file containing the expected JSON + * @param compareMode the compare mode used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isEqualToJson(File expected, JSONCompareMode compareMode) + throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotFailed(compare(expectedJson, compareMode)); + } + + /** + * Verifies that the actual value is equal to the specified JSON input stream. + * @param expected an input stream containing the expected JSON + * @param compareMode the compare mode used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isEqualToJson(InputStream expected, + JSONCompareMode compareMode) throws IOException { + return assertNotFailed(compare(this.loader.getJson(expected), compareMode)); + } + + /** + * Verifies that the actual value is equal to the specified JSON resource. + * @param expected a resource containing the expected JSON + * @param compareMode the compare mode used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isEqualToJson(Resource expected, JSONCompareMode compareMode) + throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotFailed(compare(expectedJson, compareMode)); + } + + /** + * Verifies that the actual value is equal to the specified 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 using {@code resourceLoadClass}. + * @param expected the expected JSON or the name of a resource containing the expected + * JSON + * @param comparator the comparator used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isEqualToJson(CharSequence expected, + JSONComparator comparator) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotFailed(compare(expectedJson, comparator)); + } + + /** + * Verifies that the actual value is equal to the specified JSON resource. + * @param path the name of a resource containing the expected JSON + * @param resourceLoadClass the source class used to load the resource + * @param comparator the comparator used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isEqualToJson(String path, Class resourceLoadClass, + JSONComparator comparator) throws IOException { + String expectedJson = this.loader.getJson(path, resourceLoadClass); + return assertNotFailed(compare(expectedJson, comparator)); + } + + /** + * Verifies that the actual value is equal to the specified JSON bytes. + * @param expected the expected JSON bytes + * @param comparator the comparator used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isEqualToJson(byte[] expected, JSONComparator comparator) + throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotFailed(compare(expectedJson, comparator)); + } + + /** + * Verifies that the actual value is equal to the specified JSON file. + * @param expected a file containing the expected JSON + * @param comparator the comparator used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isEqualToJson(File expected, JSONComparator comparator) + throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotFailed(compare(expectedJson, comparator)); + } + + /** + * Verifies that the actual value is equal to the specified JSON input stream. + * @param expected an input stream containing the expected JSON + * @param comparator the comparator used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isEqualToJson(InputStream expected, + JSONComparator comparator) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotFailed(compare(expectedJson, comparator)); + } + + /** + * Verifies that the actual value is equal to the specified JSON resource. + * @param expected a resource containing the expected JSON + * @param comparator the comparator used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is not equal to the given one + */ + public JsonContentAssert isEqualToJson(Resource expected, JSONComparator comparator) + throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotFailed(compare(expectedJson, comparator)); + } + + /** + * Overridden version of {@code isNotEqualTo} to perform JSON tests based on the + * object type. + * @see org.assertj.core.api.AbstractAssert#isEqualTo(java.lang.Object) + */ + @Override + public JsonContentAssert isNotEqualTo(Object expected) { + try { + if (expected == null || expected instanceof CharSequence) { + return isNotEqualToJson((CharSequence) expected); + } + if (expected instanceof byte[]) { + return isNotEqualToJson((byte[]) expected); + } + if (expected instanceof File) { + return isNotEqualToJson((File) expected); + } + if (expected instanceof InputStream) { + return isNotEqualToJson((InputStream) expected); + } + if (expected instanceof Resource) { + return isNotEqualToJson((Resource) expected); + } + throw new AssertionError( + "Unsupport type for JSON assert " + expected.getClass()); + } + catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + + /** + * Verifies that the actual value is not {@link JSONCompareMode#LENIENT leniently} + * equal to the specified 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 using + * {@code resourceLoadClass}. + * @param expected the expected JSON or the name of a resource containing the expected + * JSON + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotEqualToJson(CharSequence expected) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotPassed(compare(expectedJson, JSONCompareMode.LENIENT)); + } + + /** + * Verifies that the actual value is not {@link JSONCompareMode#LENIENT leniently} + * equal to the specified JSON resource. + * @param path the name of a resource containing the expected JSON + * @param resourceLoadClass the source class used to load the resource + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotEqualToJson(String path, Class resourceLoadClass) + throws IOException { + String expectedJson = this.loader.getJson(path, resourceLoadClass); + return assertNotPassed(compare(expectedJson, JSONCompareMode.LENIENT)); + } + + /** + * Verifies that the actual value is not {@link JSONCompareMode#LENIENT leniently} + * equal to the specified JSON bytes. + * @param expected the expected JSON bytes + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotEqualToJson(byte[] expected) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotPassed(compare(expectedJson, JSONCompareMode.LENIENT)); + } + + /** + * Verifies that the actual value is not {@link JSONCompareMode#LENIENT leniently} + * equal to the specified JSON file. + * @param expected a file containing the expected JSON + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotEqualToJson(File expected) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotPassed(compare(expectedJson, JSONCompareMode.LENIENT)); + } + + /** + * Verifies that the actual value is not {@link JSONCompareMode#LENIENT leniently} + * equal to the specified JSON input stream. + * @param expected an input stream containing the expected JSON + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotEqualToJson(InputStream expected) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotPassed(compare(expectedJson, JSONCompareMode.LENIENT)); + } + + /** + * Verifies that the actual value is not {@link JSONCompareMode#LENIENT leniently} + * equal to the specified JSON resource. + * @param expected a resource containing the expected JSON + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotEqualToJson(Resource expected) throws IOException { + return assertNotPassed( + compare(this.loader.getJson(expected), JSONCompareMode.LENIENT)); + } + + /** + * Verifies that the actual value is not {@link JSONCompareMode#STRICT strictly} equal + * to the specified 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 using + * {@code resourceLoadClass}. + * @param expected the expected JSON or the name of a resource containing the expected + * JSON + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotStrictlyEqualToJson(CharSequence expected) + throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotPassed(compare(expectedJson, JSONCompareMode.STRICT)); + } + + /** + * Verifies that the actual value is not {@link JSONCompareMode#STRICT strictly} equal + * to the specified JSON resource. + * @param path the name of a resource containing the expected JSON + * @param resourceLoadClass the source class used to load the resource + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotStrictlyEqualToJson(String path, + Class resourceLoadClass) throws IOException { + String expectedJson = this.loader.getJson(path, resourceLoadClass); + return assertNotPassed(compare(expectedJson, JSONCompareMode.STRICT)); + } + + /** + * Verifies that the actual value is not {@link JSONCompareMode#STRICT strictly} equal + * to the specified JSON bytes. + * @param expected the expected JSON bytes + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotStrictlyEqualToJson(byte[] expected) + throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotPassed(compare(expectedJson, JSONCompareMode.STRICT)); + } + + /** + * Verifies that the actual value is not {@link JSONCompareMode#STRICT strictly} equal + * to the specified JSON file. + * @param expected a file containing the expected JSON + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotStrictlyEqualToJson(File expected) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotPassed(compare(expectedJson, JSONCompareMode.STRICT)); + } + + /** + * Verifies that the actual value is not {@link JSONCompareMode#STRICT strictly} equal + * to the specified JSON input stream. + * @param expected an input stream containing the expected JSON + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotStrictlyEqualToJson(InputStream expected) + throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotPassed(compare(expectedJson, JSONCompareMode.STRICT)); + } + + /** + * Verifies that the actual value is not {@link JSONCompareMode#STRICT strictly} equal + * to the specified JSON resource. + * @param expected a resource containing the expected JSON + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotStrictlyEqualToJson(Resource expected) + throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotPassed(compare(expectedJson, JSONCompareMode.STRICT)); + } + + /** + * Verifies that the actual value is not equal to the specified 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 using {@code resourceLoadClass}. + * @param expected the expected JSON or the name of a resource containing the expected + * JSON + * @param compareMode the compare mode used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotEqualToJson(CharSequence expected, + JSONCompareMode compareMode) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotPassed(compare(expectedJson, compareMode)); + } + + /** + * Verifies that the actual value is not equal to the specified JSON resource. + * @param path the name of a resource containing the expected JSON + * @param resourceLoadClass the source class used to load the resource + * @param compareMode the compare mode used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotEqualToJson(String path, Class resourceLoadClass, + JSONCompareMode compareMode) throws IOException { + String expectedJson = this.loader.getJson(path, resourceLoadClass); + return assertNotPassed(compare(expectedJson, compareMode)); + } + + /** + * Verifies that the actual value is not equal to the specified JSON bytes. + * @param expected the expected JSON bytes + * @param compareMode the compare mode used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotEqualToJson(byte[] expected, + JSONCompareMode compareMode) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotPassed(compare(expectedJson, compareMode)); + } + + /** + * Verifies that the actual value is not equal to the specified JSON file. + * @param expected a file containing the expected JSON + * @param compareMode the compare mode used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotEqualToJson(File expected, JSONCompareMode compareMode) + throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotPassed(compare(expectedJson, compareMode)); + } + + /** + * Verifies that the actual value is not equal to the specified JSON input stream. + * @param expected an input stream containing the expected JSON + * @param compareMode the compare mode used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotEqualToJson(InputStream expected, + JSONCompareMode compareMode) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotPassed(compare(expectedJson, compareMode)); + } + + /** + * Verifies that the actual value is not equal to the specified JSON resource. + * @param expected a resource containing the expected JSON + * @param compareMode the compare mode used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotEqualToJson(Resource expected, + JSONCompareMode compareMode) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotPassed(compare(expectedJson, compareMode)); + } + + /** + * Verifies that the actual value is not equal to the specified 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 using {@code resourceLoadClass}. + * @param expected the expected JSON or the name of a resource containing the expected + * JSON + * @param comparator the comparator used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotEqualToJson(CharSequence expected, + JSONComparator comparator) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotPassed(compare(expectedJson, comparator)); + } + + /** + * Verifies that the actual value is not equal to the specified JSON resource. + * @param path the name of a resource containing the expected JSON + * @param resourceLoadClass the source class used to load the resource + * @param comparator the comparator used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotEqualToJson(String path, Class resourceLoadClass, + JSONComparator comparator) throws IOException { + String expectedJson = this.loader.getJson(path, resourceLoadClass); + return assertNotPassed(compare(expectedJson, comparator)); + } + + /** + * Verifies that the actual value is not equal to the specified JSON bytes. + * @param expected the expected JSON bytes + * @param comparator the comparator used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotEqualToJson(byte[] expected, JSONComparator comparator) + throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotPassed(compare(expectedJson, comparator)); + } + + /** + * Verifies that the actual value is not equal to the specified JSON file. + * @param expected a file containing the expected JSON + * @param comparator the comparator used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotEqualToJson(File expected, JSONComparator comparator) + throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotPassed(compare(expectedJson, comparator)); + } + + /** + * Verifies that the actual value is not equal to the specified JSON input stream. + * @param expected an input stream containing the expected JSON + * @param comparator the comparator used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotEqualToJson(InputStream expected, + JSONComparator comparator) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotPassed(compare(expectedJson, comparator)); + } + + /** + * Verifies that the actual value is not equal to the specified JSON resource. + * @param expected a resource containing the expected JSON + * @param comparator the comparator used when checking + * @return {@code this} assertion object + * @throws IOException on IO error + * @throws AssertionError if the actual JSON value is equal to the given one + */ + public JsonContentAssert isNotEqualToJson(Resource expected, + JSONComparator comparator) throws IOException { + String expectedJson = this.loader.getJson(expected); + return assertNotPassed(compare(expectedJson, comparator)); + } + + /** + * Verify that the actual value at the given JSON path produces a non-null result. If + * the JSON path expression is not {@linkplain JsonPath#isDefinite() definite}, this + * method verifies that the value at the given path is not empty. + * @param expression the {@link JsonPath} expression + * @param args arguments to parameterize the {@code JsonPath} expression with, using + * formatting specifiers defined in {@link String#format(String, Object...)} + * @return {@code this} assertion object + * @throws AssertionError if the value at the given path is missing + */ + public JsonContentAssert hasJsonPathValue(CharSequence expression, Object... args) { + new JsonPathValue(expression, args).assertHasValue(Object.class, "an object"); + return this; + } + + /** + * Verify that the actual value at the given JSON path produces a non-null string + * result. + * @param expression the {@link JsonPath} expression + * @param args arguments to parameterize the {@code JsonPath} expression with, using + * formatting specifiers defined in {@link String#format(String, Object...)} + * @return {@code this} assertion object + * @throws AssertionError if the value at the given path is missing or not a string + */ + public JsonContentAssert hasJsonPathStringValue(CharSequence expression, + Object... args) { + new JsonPathValue(expression, args).assertHasValue(String.class, "a string"); + return this; + } + + /** + * Verify that the actual value at the given JSON path produces a non-null number + * result. + * @param expression the {@link JsonPath} expression + * @param args arguments to parameterize the {@code JsonPath} expression with, using + * formatting specifiers defined in {@link String#format(String, Object...)} + * @return {@code this} assertion object + * @throws AssertionError if the value at the given path is missing or not a number + */ + public JsonContentAssert hasJsonPathNumberValue(CharSequence expression, + Object... args) { + new JsonPathValue(expression, args).assertHasValue(Number.class, "a number"); + return this; + } + + /** + * Verify that the actual value at the given JSON path produces a non-null boolean + * result. + * @param expression the {@link JsonPath} expression + * @param args arguments to parameterize the {@code JsonPath} expression with, using + * formatting specifiers defined in {@link String#format(String, Object...)} + * @return {@code this} assertion object + * @throws AssertionError if the value at the given path is missing or not a boolean + */ + public JsonContentAssert hasJsonPathBooleanValue(CharSequence expression, + Object... args) { + new JsonPathValue(expression, args).assertHasValue(Boolean.class, "a boolean"); + return this; + } + + /** + * Verify that the actual value at the given JSON path produces a non-null array + * result. + * @param expression the {@link JsonPath} expression + * @param args arguments to parameterize the {@code JsonPath} expression with, using + * formatting specifiers defined in {@link String#format(String, Object...)} + * @return {@code this} assertion object + * @throws AssertionError if the value at the given path is missing or not an array + */ + public JsonContentAssert hasJsonPathArrayValue(CharSequence expression, + Object... args) { + new JsonPathValue(expression, args).assertHasValue(List.class, "an array"); + return this; + } + + /** + * Verify that the actual value at the given JSON path produces a non-null map result. + * @param expression the {@link JsonPath} expression + * @param args arguments to parameterize the {@code JsonPath} expression with, using + * formatting specifiers defined in {@link String#format(String, Object...)} + * @return {@code this} assertion object + * @throws AssertionError if the value at the given path is missing or not a map + */ + public JsonContentAssert hasJsonPathMapValue(CharSequence expression, + Object... args) { + new JsonPathValue(expression, args).assertHasValue(Map.class, "a map"); + return this; + } + + /** + * Verify that the actual value at the given JSON path produces an + * {@link ObjectUtils#isEmpty(Object) empty} result. + * @param expression the {@link JsonPath} expression + * @param args arguments to parameterize the {@code JsonPath} expression with, using + * formatting specifiers defined in {@link String#format(String, Object...)} + * @return {@code this} assertion object + * @throws AssertionError if the value at the given path is not empty + */ + public JsonContentAssert hasEmptyJsonPathValue(CharSequence expression, + Object... args) { + new JsonPathValue(expression, args).assertHasEmptyValue(); + return this; + } + + /** + * Verify that the actual value at the given JSON path produces no result. If the JSON + * path expression is not {@linkplain JsonPath#isDefinite() definite}, this method + * verifies that the value at the given path is not empty. + * @param expression the {@link JsonPath} expression + * @param args arguments to parameterize the {@code JsonPath} expression with, using + * formatting specifiers defined in {@link String#format(String, Object...)} + * @return {@code this} assertion object + * @throws AssertionError if the value at the given path is not missing + */ + public JsonContentAssert doesNotHaveJsonPathValue(CharSequence expression, + Object... args) { + new JsonPathValue(expression, args).assertDoesNotHaveValue(); + return this; + } + + /** + * Verify that the actual value at the given JSON path does not produce an + * {@link ObjectUtils#isEmpty(Object) empty} result. + * @param expression the {@link JsonPath} expression + * @param args arguments to parameterize the {@code JsonPath} expression with, using + * formatting specifiers defined in {@link String#format(String, Object...)} + * @return {@code this} assertion object + * @throws AssertionError if the value at the given path is empty + */ + public JsonContentAssert doesNotHaveEmptyJsonPathValue(CharSequence expression, + Object... args) { + new JsonPathValue(expression, args).assertDoesNotHaveEmptyValue(); + return this; + } + + /** + * Extract the value at the given JSON path for further object assertions. + * @param expression the {@link JsonPath} expression + * @param args arguments to parameterize the {@code JsonPath} expression with, using + * formatting specifiers defined in {@link String#format(String, Object...)} + * @return a new assertion object whose object under test is the extracted item + * @throws AssertionError if the path is not valid + */ + public AbstractObjectAssert extractingJsonPathValue( + CharSequence expression, Object... args) { + return Assertions.assertThat(new JsonPathValue(expression, args).getValue(false)); + } + + /** + * Extract the string value at the given JSON path for further object assertions. + * @param expression the {@link JsonPath} expression + * @param args arguments to parameterize the {@code JsonPath} expression with, using + * formatting specifiers defined in {@link String#format(String, Object...)} + * @return a new assertion object whose object under test is the extracted item + * @throws AssertionError if the path is not valid or does not result in a string + */ + public AbstractCharSequenceAssert extractingJsonPathStringValue( + CharSequence expression, Object... args) { + return Assertions.assertThat( + extractingJsonPathValue(expression, args, String.class, "a string")); + } + + /** + * Extract the number value at the given JSON path for further object assertions. + * @param expression the {@link JsonPath} expression + * @param args arguments to parameterize the {@code JsonPath} expression with, using + * formatting specifiers defined in {@link String#format(String, Object...)} + * @return a new assertion object whose object under test is the extracted item + * @throws AssertionError if the path is not valid or does not result in a number + */ + public AbstractObjectAssert extractingJsonPathNumberValue( + CharSequence expression, Object... args) { + return Assertions.assertThat( + extractingJsonPathValue(expression, args, Number.class, "a number")); + } + + /** + * Extract the boolean value at the given JSON path for further object assertions. + * @param expression the {@link JsonPath} expression + * @param args arguments to parameterize the {@code JsonPath} expression with, using + * formatting specifiers defined in {@link String#format(String, Object...)} + * @return a new assertion object whose object under test is the extracted item + * @throws AssertionError if the path is not valid or does not result in a boolean + */ + public AbstractBooleanAssert extractingJsonPathBooleanValue( + CharSequence expression, Object... args) { + return Assertions.assertThat( + extractingJsonPathValue(expression, args, Boolean.class, "a boolean")); + } + + /** + * Extract the array value at the given JSON path for further object assertions. + * @param expression the {@link JsonPath} expression + * @param args arguments to parameterize the {@code JsonPath} expression with, using + * formatting specifiers defined in {@link String#format(String, Object...)} + * @return a new assertion object whose object under test is the extracted item + * @throws AssertionError if the path is not valid or does not result in an array + */ + @SuppressWarnings("unchecked") + public AbstractListAssert extractingJsonPathArrayValue( + CharSequence expression, Object... args) { + return Assertions.assertThat( + extractingJsonPathValue(expression, args, List.class, "an array")); + } + + /** + * Extract the map value at the given JSON path for further object assertions. + * @param expression the {@link JsonPath} expression + * @param args arguments to parameterize the {@code JsonPath} expression with, using + * formatting specifiers defined in {@link String#format(String, Object...)} + * @return a new assertion object whose object under test is the extracted item + * @throws AssertionError if the path is not valid or does not result in a map + */ + @SuppressWarnings("unchecked") + public AbstractMapAssert extractingJsonPathMapValue( + CharSequence expression, Object... args) { + return Assertions.assertThat( + extractingJsonPathValue(expression, args, Map.class, "a map")); + } + + @SuppressWarnings("unchecked") + private T extractingJsonPathValue(CharSequence expression, Object[] args, + Class type, String expectedDescription) { + JsonPathValue value = new JsonPathValue(expression, args); + if (value.getValue(false) != null) { + value.assertHasValue(type, expectedDescription); + } + return (T) value.getValue(false); + } + + private JSONCompareResult compare(CharSequence expectedJson, + JSONCompareMode compareMode) { + if (this.actual == null) { + return compareForNull(expectedJson); + } + return JSONCompare.compareJSON( + (expectedJson == null ? null : expectedJson.toString()), + this.actual.toString(), compareMode); + } + + private JSONCompareResult compare(CharSequence expectedJson, + JSONComparator comparator) { + if (this.actual == null) { + return compareForNull(expectedJson); + } + return JSONCompare.compareJSON( + (expectedJson == null ? null : expectedJson.toString()), + this.actual.toString(), comparator); + } + + private JSONCompareResult compareForNull(CharSequence expectedJson) { + JSONCompareResult result = new JSONCompareResult(); + result.passed(); + if (expectedJson != null) { + result.fail("Expected non-null JSON"); + } + return result; + } + + private JsonContentAssert assertNotFailed(JSONCompareResult result) { + if (result.failed()) { + throw new AssertionError("JSON Comparison failure: " + result.getMessage()); + } + return this; + } + + private JsonContentAssert assertNotPassed(JSONCompareResult result) { + if (result.passed()) { + throw new AssertionError("JSON Comparison failure: " + result.getMessage()); + } + return this; + } + + /** + * A {@link JsonPath} value. + */ + private class JsonPathValue { + + private final String expression; + + private final JsonPath jsonPath; + + JsonPathValue(CharSequence expression, Object... args) { + org.springframework.util.Assert.hasText( + (expression == null ? null : expression.toString()), + "expression must not be null or empty"); + this.expression = String.format(expression.toString(), args); + this.jsonPath = JsonPath.compile(this.expression); + } + + public void assertHasEmptyValue() { + if (ObjectUtils.isEmpty(getValue(false)) || isIndefiniteAndEmpty()) { + return; + } + throw new AssertionError(getExpectedValueMessage("an empty value")); + } + + public void assertDoesNotHaveEmptyValue() { + if (!ObjectUtils.isEmpty(getValue(false))) { + return; + } + throw new AssertionError(getExpectedValueMessage("a non-empty value")); + + } + + public void assertHasValue(Class type, String expectedDescription) { + Object value = getValue(true); + if (value == null || isIndefiniteAndEmpty()) { + throw new AssertionError(getNoValueMessage()); + } + if (type != null && !type.isInstance(value)) { + throw new AssertionError(getExpectedValueMessage(expectedDescription)); + } + } + + public void assertDoesNotHaveValue() { + if (getValue(false) == null || isIndefiniteAndEmpty()) { + return; + } + throw new AssertionError(getExpectedValueMessage("no value")); + } + + private boolean isIndefiniteAndEmpty() { + return !isDefinite() && isEmpty(); + } + + private boolean isDefinite() { + return this.jsonPath.isDefinite(); + } + + private boolean isEmpty() { + return ObjectUtils.isEmpty(getValue(false)); + } + + public Object getValue(boolean required) { + try { + CharSequence json = JsonContentAssert.this.actual; + return this.jsonPath.read(json == null ? null : json.toString()); + } + catch (Exception ex) { + if (!required) { + return null; + } + throw new AssertionError(getNoValueMessage() + ex.getMessage()); + } + } + + private String getNoValueMessage() { + return "No value at JSON path \"" + this.expression + "\""; + } + + private String getExpectedValueMessage(String expectedDescription) { + return String.format("Expected %s at JSON path \"%s\" but found: %s", + expectedDescription, this.expression, ObjectUtils.nullSafeToString( + StringUtils.quoteIfString(getValue(false)))); + } + + } + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonLoader.java b/spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonLoader.java new file mode 100644 index 0000000000..acb987a1d7 --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/json/JsonLoader.java @@ -0,0 +1,78 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.boot.test.json; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.util.FileCopyUtils; + +/** + * Internal helper used to load JSON from various sources. + * + * @author Phillip Webb + */ +class JsonLoader { + + private final Class resourceLoadClass; + + JsonLoader(Class resourceLoadClass) { + this.resourceLoadClass = resourceLoadClass; + } + + public Class getResourceLoadClass() { + return this.resourceLoadClass; + } + + public String getJson(CharSequence source) throws IOException { + if (source == null) { + return null; + } + if (source.toString().endsWith(".json")) { + return getJson( + new ClassPathResource(source.toString(), this.resourceLoadClass)); + } + return source.toString(); + } + + public String getJson(String path, Class resourceLoadClass) throws IOException { + return getJson(new ClassPathResource(path, resourceLoadClass)); + } + + public String getJson(byte[] source) throws IOException { + return getJson(new ByteArrayInputStream(source)); + } + + public String getJson(File source) throws IOException { + return getJson(new FileInputStream(source)); + } + + public String getJson(Resource source) throws IOException { + return getJson(source.getInputStream()); + } + + public String getJson(InputStream source) throws IOException { + return FileCopyUtils.copyToString(new InputStreamReader(source)); + } + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/json/ObjectContent.java b/spring-boot-test/src/main/java/org/springframework/boot/test/json/ObjectContent.java new file mode 100644 index 0000000000..4977835366 --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/json/ObjectContent.java @@ -0,0 +1,69 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.boot.test.json; + +import org.assertj.core.api.AssertProvider; + +import org.springframework.core.ResolvableType; +import org.springframework.util.Assert; + +/** + * Object content usually created from {@link AbstractJsonMarshalTester}.Generally used + * only to {@link AssertProvider provide} {@link ObjectContentAssert} to AssertJ + * {@code assertThat} calls. + * + * @param The content type + * @author Phillip Webb + * @since 1.4.0 + */ +public final class ObjectContent implements AssertProvider> { + + private final ResolvableType type; + + private final T object; + + /** + * Create a new {@link ObjectContent} instance. + * @param type the type under test (or {@code null} if not known) + * @param object the actual object content + */ + public ObjectContent(ResolvableType type, T object) { + Assert.notNull(object, "Object must not be null"); + this.type = type; + this.object = object; + } + + @Override + public ObjectContentAssert assertThat() { + return new ObjectContentAssert(this.object); + } + + /** + * Return the actual object content. + * @return the object content + */ + public T getObject() { + return this.object; + } + + @Override + public String toString() { + return "ObjectContent " + this.object + + (this.type == null ? "" : " created from " + this.type); + } + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/json/ObjectContentAssert.java b/spring-boot-test/src/main/java/org/springframework/boot/test/json/ObjectContentAssert.java new file mode 100644 index 0000000000..e07e7627b8 --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/json/ObjectContentAssert.java @@ -0,0 +1,63 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.boot.test.json; + +import java.util.Map; + +import org.assertj.core.api.AbstractMapAssert; +import org.assertj.core.api.AbstractObjectArrayAssert; +import org.assertj.core.api.AbstractObjectAssert; +import org.assertj.core.api.Assert; +import org.assertj.core.api.Assertions; +import org.assertj.core.internal.Objects; + +/** + * AssertJ {@link Assert} for {@link JsonContent}. + * + * @param The actual type + * @author Phillip Webb + * @since 1.4.0 + */ +public class ObjectContentAssert + extends AbstractObjectAssert, A> { + + protected ObjectContentAssert(A actual) { + super(actual, ObjectContentAssert.class); + } + + /** + * Verifies that the actual value is an array, and returns an array assertion, to + * allow chaining of array-specific assertions from this call. + * @return a list assertion object + */ + public AbstractObjectArrayAssert asArray() { + Objects.instance().assertIsInstanceOf(this.info, this.actual, Object[].class); + return Assertions.assertThat((Object[]) this.actual); + } + + /** + * Verifies that the actual value is a map, and returns a map assertion, to allow + * chaining of list-specific assertions from this call. + * @return a list assertion object + */ + @SuppressWarnings("unchecked") + public AbstractMapAssert asMap() { + Objects.instance().assertIsInstanceOf(this.info, this.actual, Map.class); + return Assertions.assertThat((Map) this.actual); + } + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/json/package-info.java b/spring-boot-test/src/main/java/org/springframework/boot/test/json/package-info.java new file mode 100644 index 0000000000..1bbb095433 --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/json/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2016 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 + * + * http://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. + */ + +/** + * Support for testing JSON. + */ +package org.springframework.boot.test.json; diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/json/AbstractJsonMarshalTesterTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/json/AbstractJsonMarshalTesterTests.java new file mode 100644 index 0000000000..3239d24459 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/json/AbstractJsonMarshalTesterTests.java @@ -0,0 +1,212 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.boot.test.json; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; + +import org.springframework.core.ResolvableType; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.Resource; +import org.springframework.util.FileCopyUtils; +import org.springframework.util.ReflectionUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link AbstractJsonMarshalTester}. + * + * @author Phillip Webb + */ +public abstract class AbstractJsonMarshalTesterTests { + + private static final String JSON = "{\"name\":\"Spring\",\"age\":123}"; + + private static final String MAP_JSON = "{\"a\":" + JSON + "}"; + + private static final String ARRAY_JSON = "[" + JSON + "]"; + + private static final ExampleObject OBJECT = createExampleObject("Spring", 123); + + private static final ResolvableType TYPE = ResolvableType + .forClass(ExampleObject.class); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Test + public void writeShouldReturnJsonContent() throws Exception { + JsonContent content = createTester(TYPE).write(OBJECT); + assertThat(content).isEqualToJson(JSON); + } + + @Test + public void writeListShouldReturnJsonContent() throws Exception { + ResolvableType type = ResolvableTypes.get("listOfExampleObject"); + List value = Collections.singletonList(OBJECT); + JsonContent content = createTester(type).write(value); + assertThat(content).isEqualToJson(ARRAY_JSON); + } + + @Test + public void writeArrayShouldReturnJsonContent() throws Exception { + ResolvableType type = ResolvableTypes.get("arrayOfExampleObject"); + ExampleObject[] value = new ExampleObject[] { OBJECT }; + JsonContent content = createTester(type).write(value); + assertThat(content).isEqualToJson(ARRAY_JSON); + } + + @Test + public void writeMapShouldReturnJsonContent() throws Exception { + ResolvableType type = ResolvableTypes.get("mapOfExampleObject"); + Map value = new LinkedHashMap(); + value.put("a", OBJECT); + JsonContent content = createTester(type).write(value); + assertThat(content).isEqualToJson(MAP_JSON); + } + + @Test + public void createWhenResourceLoadClassIsNullShouldThrowException() throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("ResourceLoadClass must not be null"); + createTester(null, ResolvableType.forClass(ExampleObject.class)); + } + + @Test + public void createWhenTypeIsNullShouldThrowException() throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Type must not be null"); + createTester(getClass(), null); + } + + @Test + public void parseBytesShouldReturnObject() throws Exception { + AbstractJsonMarshalTester tester = createTester(TYPE); + assertThat(tester.parse(JSON.getBytes())).isEqualTo(OBJECT); + } + + @Test + public void parseStringShouldReturnObject() throws Exception { + AbstractJsonMarshalTester tester = createTester(TYPE); + assertThat(tester.parse(JSON)).isEqualTo(OBJECT); + } + + @Test + public void readResourcePathShouldReturnObject() throws Exception { + AbstractJsonMarshalTester tester = createTester(TYPE); + assertThat(tester.read("example.json")).isEqualTo(OBJECT); + } + + @Test + public void readFileShouldReturnObject() throws Exception { + File file = this.temp.newFile("example.json"); + FileCopyUtils.copy(JSON.getBytes(), file); + AbstractJsonMarshalTester tester = createTester(TYPE); + assertThat(tester.read(file)).isEqualTo(OBJECT); + } + + @Test + public void readInputStreamShouldReturnObject() throws Exception { + InputStream stream = new ByteArrayInputStream(JSON.getBytes()); + AbstractJsonMarshalTester tester = createTester(TYPE); + assertThat(tester.read(stream)).isEqualTo(OBJECT); + } + + @Test + public void readResourceShouldReturnObject() throws Exception { + Resource resource = new ByteArrayResource(JSON.getBytes()); + AbstractJsonMarshalTester tester = createTester(TYPE); + assertThat(tester.read(resource)).isEqualTo(OBJECT); + } + + @Test + public void readReaderShouldReturnObject() throws Exception { + Reader reader = new StringReader(JSON); + AbstractJsonMarshalTester tester = createTester(TYPE); + assertThat(tester.read(reader)).isEqualTo(OBJECT); + } + + @Test + public void parseListShouldReturnContent() throws Exception { + ResolvableType type = ResolvableTypes.get("listOfExampleObject"); + AbstractJsonMarshalTester tester = createTester(type); + assertThat(tester.parse(ARRAY_JSON)).asList().containsOnly(OBJECT); + } + + @Test + public void parseArrayShouldReturnContent() throws Exception { + ResolvableType type = ResolvableTypes.get("arrayOfExampleObject"); + AbstractJsonMarshalTester tester = createTester(type); + assertThat(tester.parse(ARRAY_JSON)).asArray().containsOnly(OBJECT); + } + + @Test + public void parseMapShouldReturnContent() throws Exception { + ResolvableType type = ResolvableTypes.get("mapOfExampleObject"); + AbstractJsonMarshalTester tester = createTester(type); + assertThat(tester.parse(MAP_JSON)).asMap().containsEntry("a", OBJECT); + } + + protected static final ExampleObject createExampleObject(String name, int age) { + ExampleObject exampleObject = new ExampleObject(); + exampleObject.setName(name); + exampleObject.setAge(age); + return exampleObject; + } + + protected final AbstractJsonMarshalTester createTester(ResolvableType type) { + return createTester(AbstractJsonMarshalTesterTests.class, type); + } + + protected abstract AbstractJsonMarshalTester createTester( + Class resourceLoadClass, ResolvableType type); + + /** + * Access to field backed {@link ResolvableType}. + */ + public static class ResolvableTypes { + + public List listOfExampleObject; + + public ExampleObject[] arrayOfExampleObject; + + public Map mapOfExampleObject; + + public static ResolvableType get(String name) { + Field field = ReflectionUtils.findField(ResolvableTypes.class, name); + return ResolvableType.forField(field); + } + + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/json/BasicJsonTesterTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/json/BasicJsonTesterTests.java new file mode 100644 index 0000000000..a255b239ad --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/json/BasicJsonTesterTests.java @@ -0,0 +1,97 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.boot.test.json; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; + +import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.Resource; +import org.springframework.util.FileCopyUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link BasicJsonTester}. + * + * @author Phillip Webb + */ +public class BasicJsonTesterTests { + + private static final String JSON = "{\"spring\":[\"boot\",\"framework\"]}"; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + private BasicJsonTester json = new BasicJsonTester(getClass()); + + @Test + public void createWhenResourceLoadClassIsNullShouldThrowException() throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("ResourceLoadClass must not be null"); + new BasicJsonTester(null); + } + + @Test + public void fromJsonStringShouldReturnJsonContent() throws Exception { + assertThat(this.json.from(JSON)).isEqualToJson("source.json"); + } + + @Test + public void fromResourceStringShouldReturnJsonContent() throws Exception { + assertThat(this.json.from("source.json")).isEqualToJson(JSON); + } + + @Test + public void fromResourceStringWithClassShouldReturnJsonContent() throws Exception { + assertThat(this.json.from("source.json", getClass())).isEqualToJson(JSON); + } + + @Test + public void fromByteArrayShouldReturnJsonContent() throws Exception { + assertThat(this.json.from(JSON.getBytes())).isEqualToJson("source.json"); + } + + @Test + public void fromFileShouldReturnJsonContent() throws Exception { + File file = this.tempFolder.newFile("file.json"); + FileCopyUtils.copy(JSON.getBytes(), file); + assertThat(this.json.from(file)).isEqualToJson("source.json"); + } + + @Test + public void fromInputStreamShouldReturnJsonContent() throws Exception { + InputStream inputStream = new ByteArrayInputStream(JSON.getBytes()); + assertThat(this.json.from(inputStream)).isEqualToJson("source.json"); + } + + @Test + public void fromResourceShouldReturnJsonContent() throws Exception { + Resource resource = new ByteArrayResource(JSON.getBytes()); + assertThat(this.json.from(resource)).isEqualToJson("source.json"); + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/json/ExampleObject.java b/spring-boot-test/src/test/java/org/springframework/boot/test/json/ExampleObject.java new file mode 100644 index 0000000000..9ad5a00d49 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/json/ExampleObject.java @@ -0,0 +1,66 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.boot.test.json; + +import org.springframework.util.ObjectUtils; + +/** + * Example object used for serialization. + */ +public class ExampleObject { + + private String name; + + private int age; + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return this.age; + } + + public void setAge(int age) { + this.age = age; + } + + @Override + public int hashCode() { + return 0; + } + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != getClass()) { + return false; + } + ExampleObject other = (ExampleObject) obj; + return ObjectUtils.nullSafeEquals(this.name, other.name) + && ObjectUtils.nullSafeEquals(this.age, other.age); + } + + @Override + public String toString() { + return this.name + " " + this.age; + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/json/GsonTesterTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/json/GsonTesterTests.java new file mode 100644 index 0000000000..6ff222b07e --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/json/GsonTesterTests.java @@ -0,0 +1,89 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.boot.test.json; + +import java.util.List; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import org.junit.Test; + +import org.springframework.core.ResolvableType; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link GsonTester}. + * + * @author Phillip Webb + */ +public class GsonTesterTests extends AbstractJsonMarshalTesterTests { + + @Test + public void initFieldsWhenTestIsNullShouldThrowException() { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("TestInstance must not be null"); + GsonTester.initFields(null, new GsonBuilder().create()); + } + + @Test + public void initFieldsWhenObjectMapperIsNullShouldThrowException() { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Marshaller must not be null"); + GsonTester.initFields(new InitFieldsTestClass(), (Gson) null); + } + + @Test + public void initFieldsShouldSetNullFields() { + InitFieldsTestClass test = new InitFieldsTestClass(); + assertThat(test.test).isNull(); + assertThat(test.base).isNull(); + GsonTester.initFields(test, new GsonBuilder().create()); + assertThat(test.test).isNotNull(); + assertThat(test.base).isNotNull(); + assertThat(test.test.getType().resolve()).isEqualTo(List.class); + assertThat(test.test.getType().resolveGeneric()).isEqualTo(ExampleObject.class); + } + + @Override + protected AbstractJsonMarshalTester createTester(Class resourceLoadClass, + ResolvableType type) { + return new GsonTester(resourceLoadClass, type, + new GsonBuilder().create()); + } + + static abstract class InitFieldsBaseClass { + + public GsonTester base; + + public GsonTester baseSet = new GsonTester( + InitFieldsBaseClass.class, ResolvableType.forClass(ExampleObject.class), + new GsonBuilder().create()); + + } + + static class InitFieldsTestClass extends InitFieldsBaseClass { + + public GsonTester> test; + + public GsonTester testSet = new GsonTester( + InitFieldsBaseClass.class, ResolvableType.forClass(ExampleObject.class), + new GsonBuilder().create()); + + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/json/JacksonTesterIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/json/JacksonTesterIntegrationTests.java new file mode 100644 index 0000000000..f85532cad2 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/json/JacksonTesterIntegrationTests.java @@ -0,0 +1,62 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.boot.test.json; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Before; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link JacksonTester}. Shows typical usage. + * + * @author Phillip Webb + */ +public class JacksonTesterIntegrationTests { + + private JacksonTester> listJson; + + private JacksonTester> mapJson; + + @Before + public void setup() { + JacksonTester.initFields(this, new ObjectMapper()); + } + + @Test + public void typicalListTest() throws Exception { + String example = "[{\"name\":\"Spring\",\"age\":123}]"; + 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); + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/json/JacksonTesterTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/json/JacksonTesterTests.java new file mode 100644 index 0000000000..dc28dbf57b --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/json/JacksonTesterTests.java @@ -0,0 +1,87 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.boot.test.json; + +import java.util.List; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Test; + +import org.springframework.core.ResolvableType; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link JacksonTester}. + * + * @author Phillip Webb + */ +public class JacksonTesterTests extends AbstractJsonMarshalTesterTests { + + @Test + public void initFieldsWhenTestIsNullShouldThrowException() { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("TestInstance must not be null"); + JacksonTester.initFields(null, new ObjectMapper()); + } + + @Test + public void initFieldsWhenObjectMapperIsNullShouldThrowException() { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Marshaller must not be null"); + JacksonTester.initFields(new InitFieldsTestClass(), (ObjectMapper) null); + } + + @Test + public void initFieldsShouldSetNullFields() { + InitFieldsTestClass test = new InitFieldsTestClass(); + assertThat(test.test).isNull(); + assertThat(test.base).isNull(); + JacksonTester.initFields(test, new ObjectMapper()); + assertThat(test.test).isNotNull(); + assertThat(test.base).isNotNull(); + assertThat(test.test.getType().resolve()).isEqualTo(List.class); + assertThat(test.test.getType().resolveGeneric()).isEqualTo(ExampleObject.class); + } + + @Override + protected AbstractJsonMarshalTester createTester(Class resourceLoadClass, + ResolvableType type) { + return new JacksonTester(resourceLoadClass, type, new ObjectMapper()); + } + + static abstract class InitFieldsBaseClass { + + public JacksonTester base; + + public JacksonTester baseSet = new JacksonTester( + InitFieldsBaseClass.class, ResolvableType.forClass(ExampleObject.class), + new ObjectMapper()); + + } + + static class InitFieldsTestClass extends InitFieldsBaseClass { + + public JacksonTester> test; + + public JacksonTester testSet = new JacksonTester( + InitFieldsBaseClass.class, ResolvableType.forClass(ExampleObject.class), + new ObjectMapper()); + + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/json/JsonContentAssertTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/json/JsonContentAssertTests.java new file mode 100644 index 0000000000..fd8f4c2ad8 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/json/JsonContentAssertTests.java @@ -0,0 +1,1339 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.boot.test.json; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import org.assertj.core.api.AssertProvider; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +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.Resource; +import org.springframework.test.util.JsonPathExpectationsHelper; +import org.springframework.util.FileCopyUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link JsonContentAssert}. Some tests here are based on Spring Framework + * tests for {@link JsonPathExpectationsHelper}. + * + * @author Phillip Webb + */ +public 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 String TYPES = loadJson("types.json"); + + private static final String SIMPSONS = loadJson("simpsons.json"); + + private static JSONComparator COMPARATOR = new DefaultComparator( + JSONCompareMode.LENIENT); + + @Rule + public final ExpectedException thrown = ExpectedException.none(); + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + @Test + public void isEqualToWhenStringIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isEqualTo(LENIENT_SAME); + } + + @Test(expected = AssertionError.class) + public void isEqualToWhenNullActualShouldFail() throws Exception { + assertThat(forJson(null)).isEqualTo(SOURCE); + } + + @Test(expected = AssertionError.class) + public void isEqualToWhenStringIsNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isEqualTo(DIFFERENT); + } + + @Test + public void isEqualToWhenResourcePathIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isEqualTo("lenient-same.json"); + } + + @Test(expected = AssertionError.class) + public void isEqualToWhenResourcePathIsNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isEqualTo("different.json"); + } + + @Test + public void isEqualToWhenBytesAreMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isEqualTo(LENIENT_SAME.getBytes()); + } + + @Test(expected = AssertionError.class) + public void isEqualToWhenBytesAreNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isEqualTo(DIFFERENT.getBytes()); + } + + @Test + public void isEqualToWhenFileIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isEqualTo(createFile(LENIENT_SAME)); + } + + @Test(expected = AssertionError.class) + public void isEqualToWhenFileIsNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isEqualTo(createFile(DIFFERENT)); + } + + @Test + public void isEqualToWhenInputStreamIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isEqualTo(createInputStream(LENIENT_SAME)); + } + + @Test(expected = AssertionError.class) + public void isEqualToWhenInputStreamIsNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isEqualTo(createInputStream(DIFFERENT)); + } + + @Test + public void isEqualToWhenResourceIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isEqualTo(createResource(LENIENT_SAME)); + } + + @Test(expected = AssertionError.class) + public void isEqualToWhenResourceIsNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isEqualTo(createResource(DIFFERENT)); + } + + @Test + public void isEqualToJsonWhenStringIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(LENIENT_SAME); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenNullActualShouldFail() throws Exception { + assertThat(forJson(null)).isEqualToJson(SOURCE); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenStringIsNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(DIFFERENT); + } + + @Test + public void isEqualToJsonWhenResourcePathIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson("lenient-same.json"); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenResourcePathIsNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson("different.json"); + } + + @Test + public void isEqualToJsonWhenResourcePathAndClassIsMatchingShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson("lenient-same.json", getClass()); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenResourcePathAndClassIsNotMatchingShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson("different.json", getClass()); + } + + @Test + public void isEqualToJsonWhenBytesAreMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(LENIENT_SAME.getBytes()); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenBytesAreNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(DIFFERENT.getBytes()); + } + + @Test + public void isEqualToJsonWhenFileIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(createFile(LENIENT_SAME)); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenFileIsNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(createFile(DIFFERENT)); + } + + @Test + public void isEqualToJsonWhenInputStreamIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(createInputStream(LENIENT_SAME)); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenInputStreamIsNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(createInputStream(DIFFERENT)); + } + + @Test + public void isEqualToJsonWhenResourceIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(createResource(LENIENT_SAME)); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenResourceIsNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(createResource(DIFFERENT)); + } + + @Test + public void isStrictlyEqualToJsonWhenStringIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isStrictlyEqualToJson(SOURCE); + } + + @Test(expected = AssertionError.class) + public void isStrictlyEqualToJsonWhenStringIsNotMatchingShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isStrictlyEqualToJson(LENIENT_SAME); + } + + @Test + public void isStrictlyEqualToJsonWhenResourcePathIsMatchingShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isStrictlyEqualToJson("source.json"); + } + + @Test(expected = AssertionError.class) + public void isStrictlyEqualToJsonWhenResourcePathIsNotMatchingShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isStrictlyEqualToJson("lenient-same.json"); + } + + @Test + public void isStrictlyEqualToJsonWhenResourcePathAndClassIsMatchingShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isStrictlyEqualToJson("source.json", getClass()); + } + + @Test(expected = AssertionError.class) + public void isStrictlyEqualToJsonWhenResourcePathAndClassIsNotMatchingShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isStrictlyEqualToJson("lenient-same.json", + getClass()); + } + + @Test + public void isStrictlyEqualToJsonWhenBytesAreMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isStrictlyEqualToJson(SOURCE.getBytes()); + } + + @Test(expected = AssertionError.class) + public void isStrictlyEqualToJsonWhenBytesAreNotMatchingShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isStrictlyEqualToJson(LENIENT_SAME.getBytes()); + } + + @Test + public void isStrictlyEqualToJsonWhenFileIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isStrictlyEqualToJson(createFile(SOURCE)); + } + + @Test(expected = AssertionError.class) + public void isStrictlyEqualToJsonWhenFileIsNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isStrictlyEqualToJson(createFile(LENIENT_SAME)); + } + + @Test + public void isStrictlyEqualToJsonWhenInputStreamIsMatchingShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isStrictlyEqualToJson(createInputStream(SOURCE)); + } + + @Test(expected = AssertionError.class) + public void isStrictlyEqualToJsonWhenInputStreamIsNotMatchingShouldFail() + throws Exception { + assertThat(forJson(SOURCE)) + .isStrictlyEqualToJson(createInputStream(LENIENT_SAME)); + } + + @Test + public void isStrictlyEqualToJsonWhenResourceIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isStrictlyEqualToJson(createResource(SOURCE)); + } + + @Test(expected = AssertionError.class) + public void isStrictlyEqualToJsonWhenResourceIsNotMatchingShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isStrictlyEqualToJson(createResource(LENIENT_SAME)); + } + + @Test + public void isEqualToJsonWhenStringIsMatchingAndLienientShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(LENIENT_SAME, JSONCompareMode.LENIENT); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenStringIsNotMatchingAndLienientShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(DIFFERENT, JSONCompareMode.LENIENT); + } + + @Test + public void isEqualToJsonWhenResourcePathIsMatchingAndLienientShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson("lenient-same.json", + JSONCompareMode.LENIENT); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenResourcePathIsNotMatchingAndLienientShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson("different.json", + JSONCompareMode.LENIENT); + } + + @Test + public void isEqualToJsonWhenResourcePathAndClassIsMatchingAndLienientShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson("lenient-same.json", getClass(), + JSONCompareMode.LENIENT); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenResourcePathAndClassIsNotMatchingAndLienientShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson("different.json", getClass(), + JSONCompareMode.LENIENT); + } + + @Test + public void isEqualToJsonWhenBytesAreMatchingAndLienientShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(LENIENT_SAME.getBytes(), + JSONCompareMode.LENIENT); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenBytesAreNotMatchingAndLienientShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(DIFFERENT.getBytes(), + JSONCompareMode.LENIENT); + } + + @Test + public void isEqualToJsonWhenFileIsMatchingAndLienientShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(createFile(LENIENT_SAME), + JSONCompareMode.LENIENT); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenFileIsNotMatchingAndLienientShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(createFile(DIFFERENT), + JSONCompareMode.LENIENT); + } + + @Test + public void isEqualToJsonWhenInputStreamIsMatchingAndLienientShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(createInputStream(LENIENT_SAME), + JSONCompareMode.LENIENT); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenInputStreamIsNotMatchingAndLienientShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(createInputStream(DIFFERENT), + JSONCompareMode.LENIENT); + } + + @Test + public void isEqualToJsonWhenResourceIsMatchingAndLienientShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(createResource(LENIENT_SAME), + JSONCompareMode.LENIENT); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenResourceIsNotMatchingAndLienientShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(createResource(DIFFERENT), + JSONCompareMode.LENIENT); + } + + @Test + public void isEqualToJsonWhenStringIsMatchingAndComparatorShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(LENIENT_SAME, + JsonContentAssertTests.COMPARATOR); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenStringIsNotMatchingAndComparatorShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(DIFFERENT, + JsonContentAssertTests.COMPARATOR); + } + + @Test + public void isEqualToJsonWhenResourcePathIsMatchingAndComparatorShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson("lenient-same.json", + JsonContentAssertTests.COMPARATOR); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenResourcePathIsNotMatchingAndComparatorShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson("different.json", + JsonContentAssertTests.COMPARATOR); + } + + @Test + public void isEqualToJsonWhenResourcePathAndClassIsMatchingAndComparatorShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson("lenient-same.json", getClass(), + JsonContentAssertTests.COMPARATOR); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenResourcePathAndClassIsNotMatchingAndComparatorShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson("different.json", getClass(), + JsonContentAssertTests.COMPARATOR); + } + + @Test + public void isEqualToJsonWhenBytesAreMatchingAndComparatorShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(LENIENT_SAME.getBytes(), + JsonContentAssertTests.COMPARATOR); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenBytesAreNotMatchingAndComparatorShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(DIFFERENT.getBytes(), + JsonContentAssertTests.COMPARATOR); + } + + @Test + public void isEqualToJsonWhenFileIsMatchingAndComparatorShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(createFile(LENIENT_SAME), + JsonContentAssertTests.COMPARATOR); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenFileIsNotMatchingAndComparatorShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(createFile(DIFFERENT), + JsonContentAssertTests.COMPARATOR); + } + + @Test + public void isEqualToJsonWhenInputStreamIsMatchingAndComparatorShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(createInputStream(LENIENT_SAME), + JsonContentAssertTests.COMPARATOR); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenInputStreamIsNotMatchingAndComparatorShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(createInputStream(DIFFERENT), + JsonContentAssertTests.COMPARATOR); + } + + @Test + public void isEqualToJsonWhenResourceIsMatchingAndComparatorShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(createResource(LENIENT_SAME), + JsonContentAssertTests.COMPARATOR); + } + + @Test(expected = AssertionError.class) + public void isEqualToJsonWhenResourceIsNotMatchingAndComparatorShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isEqualToJson(createResource(DIFFERENT), + JsonContentAssertTests.COMPARATOR); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToWhenStringIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualTo(LENIENT_SAME); + } + + @Test + public void isNotEqualToWhenNullActualShouldPass() throws Exception { + assertThat(forJson(null)).isNotEqualTo(SOURCE); + } + + @Test + public void isNotEqualToWhenStringIsNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualTo(DIFFERENT); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToWhenResourcePathIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualTo("lenient-same.json"); + } + + @Test + public void isNotEqualToWhenResourcePathIsNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualTo("different.json"); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToWhenBytesAreMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualTo(LENIENT_SAME.getBytes()); + } + + @Test + public void isNotEqualToWhenBytesAreNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualTo(DIFFERENT.getBytes()); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToWhenFileIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualTo(createFile(LENIENT_SAME)); + } + + @Test + public void isNotEqualToWhenFileIsNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualTo(createFile(DIFFERENT)); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToWhenInputStreamIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualTo(createInputStream(LENIENT_SAME)); + } + + @Test + public void isNotEqualToWhenInputStreamIsNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualTo(createInputStream(DIFFERENT)); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToWhenResourceIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualTo(createResource(LENIENT_SAME)); + } + + @Test + public void isNotEqualToWhenResourceIsNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualTo(createResource(DIFFERENT)); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToJsonWhenStringIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(LENIENT_SAME); + } + + @Test + public void isNotEqualToJsonWhenNullActualShouldPass() throws Exception { + assertThat(forJson(null)).isNotEqualToJson(SOURCE); + } + + @Test + public void isNotEqualToJsonWhenStringIsNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(DIFFERENT); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToJsonWhenResourcePathIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson("lenient-same.json"); + } + + @Test + public void isNotEqualToJsonWhenResourcePathIsNotMatchingShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson("different.json"); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToJsonWhenResourcePathAndClassIsMatchingShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson("lenient-same.json", getClass()); + } + + @Test + public void isNotEqualToJsonWhenResourcePathAndClassIsNotMatchingShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson("different.json", getClass()); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToJsonWhenBytesAreMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(LENIENT_SAME.getBytes()); + } + + @Test + public void isNotEqualToJsonWhenBytesAreNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(DIFFERENT.getBytes()); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToJsonWhenFileIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(createFile(LENIENT_SAME)); + } + + @Test + public void isNotEqualToJsonWhenFileIsNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(createFile(DIFFERENT)); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToJsonWhenInputStreamIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(createInputStream(LENIENT_SAME)); + } + + @Test + public void isNotEqualToJsonWhenInputStreamIsNotMatchingShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(createInputStream(DIFFERENT)); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToJsonWhenResourceIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(createResource(LENIENT_SAME)); + } + + @Test + public void isNotEqualToJsonWhenResourceIsNotMatchingShouldFail() throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(createResource(DIFFERENT)); + } + + @Test(expected = AssertionError.class) + public void isNotStrictlyEqualToJsonWhenStringIsMatchingShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isNotStrictlyEqualToJson(SOURCE); + } + + @Test + public void isNotStrictlyEqualToJsonWhenStringIsNotMatchingShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotStrictlyEqualToJson(LENIENT_SAME); + } + + @Test(expected = AssertionError.class) + public void isNotStrictlyEqualToJsonWhenResourcePathIsMatchingShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isNotStrictlyEqualToJson("source.json"); + } + + @Test + public void isNotStrictlyEqualToJsonWhenResourcePathIsNotMatchingShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotStrictlyEqualToJson("lenient-same.json"); + } + + @Test(expected = AssertionError.class) + public void isNotStrictlyEqualToJsonWhenResourcePathAndClassIsMatchingShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isNotStrictlyEqualToJson("source.json", getClass()); + } + + @Test + public void isNotStrictlyEqualToJsonWhenResourcePathAndClassIsNotMatchingShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotStrictlyEqualToJson("lenient-same.json", + getClass()); + } + + @Test(expected = AssertionError.class) + public void isNotStrictlyEqualToJsonWhenBytesAreMatchingShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isNotStrictlyEqualToJson(SOURCE.getBytes()); + } + + @Test + public void isNotStrictlyEqualToJsonWhenBytesAreNotMatchingShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotStrictlyEqualToJson(LENIENT_SAME.getBytes()); + } + + @Test(expected = AssertionError.class) + public void isNotStrictlyEqualToJsonWhenFileIsMatchingShouldPass() throws Exception { + assertThat(forJson(SOURCE)).isNotStrictlyEqualToJson(createFile(SOURCE)); + } + + @Test + public void isNotStrictlyEqualToJsonWhenFileIsNotMatchingShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotStrictlyEqualToJson(createFile(LENIENT_SAME)); + } + + @Test(expected = AssertionError.class) + public void isNotStrictlyEqualToJsonWhenInputStreamIsMatchingShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isNotStrictlyEqualToJson(createInputStream(SOURCE)); + } + + @Test + public void isNotStrictlyEqualToJsonWhenInputStreamIsNotMatchingShouldFail() + throws Exception { + assertThat(forJson(SOURCE)) + .isNotStrictlyEqualToJson(createInputStream(LENIENT_SAME)); + } + + @Test(expected = AssertionError.class) + public void isNotStrictlyEqualToJsonWhenResourceIsMatchingShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isNotStrictlyEqualToJson(createResource(SOURCE)); + } + + @Test + public void isNotStrictlyEqualToJsonWhenResourceIsNotMatchingShouldFail() + throws Exception { + assertThat(forJson(SOURCE)) + .isNotStrictlyEqualToJson(createResource(LENIENT_SAME)); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToJsonWhenStringIsMatchingAndLienientShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(LENIENT_SAME, + JSONCompareMode.LENIENT); + } + + @Test + public void isNotEqualToJsonWhenStringIsNotMatchingAndLienientShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(DIFFERENT, JSONCompareMode.LENIENT); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToJsonWhenResourcePathIsMatchingAndLienientShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson("lenient-same.json", + JSONCompareMode.LENIENT); + } + + @Test + public void isNotEqualToJsonWhenResourcePathIsNotMatchingAndLienientShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson("different.json", + JSONCompareMode.LENIENT); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToJsonWhenResourcePathAndClassIsMatchingAndLienientShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson("lenient-same.json", getClass(), + JSONCompareMode.LENIENT); + } + + @Test + public void isNotEqualToJsonWhenResourcePathAndClassIsNotMatchingAndLienientShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson("different.json", getClass(), + JSONCompareMode.LENIENT); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToJsonWhenBytesAreMatchingAndLienientShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(LENIENT_SAME.getBytes(), + JSONCompareMode.LENIENT); + } + + @Test + public void isNotEqualToJsonWhenBytesAreNotMatchingAndLienientShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(DIFFERENT.getBytes(), + JSONCompareMode.LENIENT); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToJsonWhenFileIsMatchingAndLienientShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(createFile(LENIENT_SAME), + JSONCompareMode.LENIENT); + } + + @Test + public void isNotEqualToJsonWhenFileIsNotMatchingAndLienientShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(createFile(DIFFERENT), + JSONCompareMode.LENIENT); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToJsonWhenInputStreamIsMatchingAndLienientShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(createInputStream(LENIENT_SAME), + JSONCompareMode.LENIENT); + } + + @Test + public void isNotEqualToJsonWhenInputStreamIsNotMatchingAndLienientShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(createInputStream(DIFFERENT), + JSONCompareMode.LENIENT); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToJsonWhenResourceIsMatchingAndLienientShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(createResource(LENIENT_SAME), + JSONCompareMode.LENIENT); + } + + @Test + public void isNotEqualToJsonWhenResourceIsNotMatchingAndLienientShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(createResource(DIFFERENT), + JSONCompareMode.LENIENT); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToJsonWhenStringIsMatchingAndComparatorShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(LENIENT_SAME, + JsonContentAssertTests.COMPARATOR); + } + + @Test + public void isNotEqualToJsonWhenStringIsNotMatchingAndComparatorShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(DIFFERENT, + JsonContentAssertTests.COMPARATOR); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToJsonWhenResourcePathIsMatchingAndComparatorShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson("lenient-same.json", + JsonContentAssertTests.COMPARATOR); + } + + @Test + public void isNotEqualToJsonWhenResourcePathIsNotMatchingAndComparatorShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson("different.json", + JsonContentAssertTests.COMPARATOR); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToJsonWhenResourcePathIsMatchingAndClassAndComparatorShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson("lenient-same.json", getClass(), + JsonContentAssertTests.COMPARATOR); + } + + @Test + public void isNotEqualToJsonWhenResourcePathAndClassAndComparatorIsNotMatchingShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson("different.json", getClass(), + JsonContentAssertTests.COMPARATOR); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToJsonWhenBytesAreMatchingAndComparatorShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(LENIENT_SAME.getBytes(), + JsonContentAssertTests.COMPARATOR); + } + + @Test + public void isNotEqualToJsonWhenBytesAreNotMatchingAndComparatorShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(DIFFERENT.getBytes(), + JsonContentAssertTests.COMPARATOR); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToJsonWhenFileIsMatchingAndComparatorShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(createFile(LENIENT_SAME), + JsonContentAssertTests.COMPARATOR); + } + + @Test + public void isNotEqualToJsonWhenFileIsNotMatchingAndComparatorShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(createFile(DIFFERENT), + JsonContentAssertTests.COMPARATOR); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToJsonWhenInputStreamIsMatchingAndComparatorShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(createInputStream(LENIENT_SAME), + JsonContentAssertTests.COMPARATOR); + } + + @Test + public void isNotEqualToJsonWhenInputStreamIsNotMatchingAndComparatorShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(createInputStream(DIFFERENT), + JsonContentAssertTests.COMPARATOR); + } + + @Test(expected = AssertionError.class) + public void isNotEqualToJsonWhenResourceIsMatchingAndComparatorShouldPass() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(createResource(LENIENT_SAME), + JsonContentAssertTests.COMPARATOR); + } + + @Test + public void isNotEqualToJsonWhenResourceIsNotMatchingAndComparatorShouldFail() + throws Exception { + assertThat(forJson(SOURCE)).isNotEqualToJson(createResource(DIFFERENT), + JsonContentAssertTests.COMPARATOR); + } + + @Test + public void hasJsonPathValue() throws Exception { + System.out.println(TYPES.replace("'", "\"")); + System.out.println(SIMPSONS.replace("'", "\"")); + assertThat(forJson(TYPES)).hasJsonPathValue("$.str"); + } + + @Test + public void hasJsonPathValueForAnEmptyArray() throws Exception { + assertThat(forJson(TYPES)).hasJsonPathValue("$.emptyArray"); + } + + @Test + public void hasJsonPathValueForAnEmptyMap() throws Exception { + assertThat(forJson(TYPES)).hasJsonPathValue("$.emptyMap"); + } + + @Test + public void hasJsonPathValueForIndefinatePathWithResults() throws Exception { + assertThat(forJson(SIMPSONS)) + .hasJsonPathValue("$.familyMembers[?(@.name == 'Bart')]"); + } + + @Test + public void hasJsonPathValueForIndefinatePathWithEmptyResults() throws Exception { + String expression = "$.familyMembers[?(@.name == 'Dilbert')]"; + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("No value at JSON path \"" + expression + "\""); + assertThat(forJson(SIMPSONS)).hasJsonPathValue(expression); + } + + @Test + public void doesNotHaveJsonPathValue() throws Exception { + assertThat(forJson(TYPES)).doesNotHaveJsonPathValue("$.bogus"); + } + + @Test + public void doesNotHaveJsonPathValueForAnEmptyArray() throws Exception { + String expression = "$.emptyArray"; + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage( + "Expected no value at JSON path \"" + expression + "\" but found: []"); + assertThat(forJson(TYPES)).doesNotHaveJsonPathValue(expression); + } + + @Test + public void doesNotHaveJsonPathValueForAnEmptyMap() throws Exception { + String expression = "$.emptyMap"; + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage( + "Expected no value at JSON path \"" + expression + "\" but found: {}"); + assertThat(forJson(TYPES)).doesNotHaveJsonPathValue(expression); + } + + @Test + public void doesNotHaveJsonPathValueForIndefinatePathWithResults() throws Exception { + String expression = "$.familyMembers[?(@.name == 'Bart')]"; + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("Expected no value at JSON path \"" + expression + + "\" but found: [{\"name\":\"Bart\"}]"); + assertThat(forJson(SIMPSONS)).doesNotHaveJsonPathValue(expression); + } + + @Test + public void doesNotHaveJsonPathValueForIndefinatePathWithEmptyResults() + throws Exception { + assertThat(forJson(SIMPSONS)) + .doesNotHaveJsonPathValue("$.familyMembers[?(@.name == 'Dilbert')]"); + } + + @Test + public void hasEmptyJsonPathValueForAnEmptyString() throws Exception { + assertThat(forJson(TYPES)).hasEmptyJsonPathValue("$.emptyString"); + } + + @Test + public void hasEmptyJsonPathValueForAnEmptyArray() throws Exception { + assertThat(forJson(TYPES)).hasEmptyJsonPathValue("$.emptyArray"); + } + + @Test + public void hasEmptyJsonPathValueForAnEmptyMap() throws Exception { + assertThat(forJson(TYPES)).hasEmptyJsonPathValue("$.emptyMap"); + } + + @Test + public void hasEmptyJsonPathValueForIndefinatePathWithEmptyResults() + throws Exception { + assertThat(forJson(SIMPSONS)) + .hasEmptyJsonPathValue("$.familyMembers[?(@.name == 'Dilbert')]"); + } + + @Test + public void hasEmptyJsonPathValueForIndefinatePathWithResults() throws Exception { + String expression = "$.familyMembers[?(@.name == 'Bart')]"; + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("Expected an empty value at JSON path \"" + expression + + "\" but found: [{\"name\":\"Bart\"}]"); + assertThat(forJson(SIMPSONS)).hasEmptyJsonPathValue(expression); + } + + @Test + public void hasEmptyJsonPathValueForWhitespace() throws Exception { + String expression = "$.whitespace"; + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("Expected an empty value at JSON path \"" + expression + + "\" but found: ' '"); + assertThat(forJson(TYPES)).hasEmptyJsonPathValue(expression); + } + + @Test + public void doesNotHaveEmptyJsonPathValueForString() throws Exception { + assertThat(forJson(TYPES)).doesNotHaveEmptyJsonPathValue("$.str"); + } + + @Test + public void doesNotHaveEmptyJsonPathValueForNumber() throws Exception { + assertThat(forJson(TYPES)).doesNotHaveEmptyJsonPathValue("$.num"); + } + + @Test + public void doesNotHaveEmptyJsonPathValueForBoolean() throws Exception { + assertThat(forJson(TYPES)).doesNotHaveEmptyJsonPathValue("$.bool"); + } + + @Test + public void doesNotHaveEmptyJsonPathValueForArray() throws Exception { + assertThat(forJson(TYPES)).doesNotHaveEmptyJsonPathValue("$.arr"); + } + + @Test + public void doesNotHaveEmptyJsonPathValueForMap() throws Exception { + assertThat(forJson(TYPES)).doesNotHaveEmptyJsonPathValue("$.colorMap"); + } + + @Test + public void doesNotHaveEmptyJsonPathValueForIndefinatePathWithResults() + throws Exception { + assertThat(forJson(SIMPSONS)) + .doesNotHaveEmptyJsonPathValue("$.familyMembers[?(@.name == 'Bart')]"); + } + + @Test + public void doesNotHaveEmptyJsonPathValueForIndefinatePathWithEmptyResults() + throws Exception { + String expression = "$.familyMembers[?(@.name == 'Dilbert')]"; + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("Expected a non-empty value at JSON path \"" + + expression + "\" but found: []"); + assertThat(forJson(SIMPSONS)).doesNotHaveEmptyJsonPathValue(expression); + } + + @Test + public void doesNotHaveEmptyJsonPathValueForAnEmptyString() throws Exception { + String expression = "$.emptyString"; + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("Expected a non-empty value at JSON path \"" + + expression + "\" but found: ''"); + assertThat(forJson(TYPES)).doesNotHaveEmptyJsonPathValue(expression); + } + + @Test + public void doesNotHaveEmptyJsonPathValueForForAnEmptyArray() throws Exception { + String expression = "$.emptyArray"; + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("Expected a non-empty value at JSON path \"" + + expression + "\" but found: []"); + assertThat(forJson(TYPES)).doesNotHaveEmptyJsonPathValue(expression); + } + + @Test + public void doesNotHaveEmptyJsonPathValueForAnEmptyMap() throws Exception { + String expression = "$.emptyMap"; + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("Expected a non-empty value at JSON path \"" + + expression + "\" but found: {}"); + assertThat(forJson(TYPES)).doesNotHaveEmptyJsonPathValue(expression); + } + + @Test + public void hasJsonPathStringValue() throws Exception { + assertThat(forJson(TYPES)).hasJsonPathStringValue("$.str"); + } + + @Test + public void hasJsonPathStringValueForAnEmptyString() throws Exception { + assertThat(forJson(TYPES)).hasJsonPathStringValue("$.emptyString"); + } + + @Test + public void hasJsonPathStringValueForAnEmptyStringForNonString() throws Exception { + String expression = "$.bool"; + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage( + "Expected a string at JSON path \"" + expression + "\" but found: true"); + assertThat(forJson(TYPES)).hasJsonPathStringValue(expression); + } + + @Test + public void hasJsonPathNumberValue() throws Exception { + assertThat(forJson(TYPES)).hasJsonPathNumberValue("$.num"); + } + + @Test + public void hasJsonPathNumberValueForNonNumber() throws Exception { + String expression = "$.bool"; + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage( + "Expected a number at JSON path \"" + expression + "\" but found: true"); + assertThat(forJson(TYPES)).hasJsonPathNumberValue(expression); + } + + @Test + public void hasJsonPathBooleanValue() throws Exception { + assertThat(forJson(TYPES)).hasJsonPathBooleanValue("$.bool"); + } + + @Test + public void hasJsonPathBooleanValueForNonBoolean() throws Exception { + String expression = "$.num"; + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage( + "Expected a boolean at JSON path \"" + expression + "\" but found: 5"); + assertThat(forJson(TYPES)).hasJsonPathBooleanValue(expression); + } + + @Test + public void hasJsonPathArrayValue() throws Exception { + assertThat(forJson(TYPES)).hasJsonPathArrayValue("$.arr"); + } + + @Test + public void hasJsonPathArrayValueForAnEmptyArray() throws Exception { + assertThat(forJson(TYPES)).hasJsonPathArrayValue("$.emptyArray"); + } + + @Test + public void hasJsonPathArrayValueForNonArray() throws Exception { + String expression = "$.str"; + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage( + "Expected an array at JSON path \"" + expression + "\" but found: 'foo'"); + assertThat(forJson(TYPES)).hasJsonPathArrayValue(expression); + } + + @Test + public void assertValueIsMap() throws Exception { + assertThat(forJson(TYPES)).hasJsonPathMapValue("$.colorMap"); + } + + @Test + public void assertValueIsMapForAnEmptyMap() throws Exception { + assertThat(forJson(TYPES)).hasJsonPathMapValue("$.emptyMap"); + } + + @Test + public void assertValueIsMapForNonMap() throws Exception { + String expression = "$.str"; + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage( + "Expected a map at JSON path \"" + expression + "\" but found: 'foo'"); + assertThat(forJson(TYPES)).hasJsonPathMapValue(expression); + } + + @Test + public void extractingJsonPathValue() throws Exception { + assertThat(forJson(TYPES)).extractingJsonPathValue("@.str").isEqualTo("foo"); + } + + @Test + public void extractingJsonPathValueForMissing() throws Exception { + assertThat(forJson(TYPES)).extractingJsonPathValue("@.bogus").isNull(); + } + + @Test + public void extractingJsonPathStringValue() throws Exception { + assertThat(forJson(TYPES)).extractingJsonPathStringValue("@.str") + .isEqualTo("foo"); + } + + @Test + public void extractingJsonPathStringValueForMissing() throws Exception { + assertThat(forJson(TYPES)).extractingJsonPathStringValue("@.bogus").isNull(); + } + + @Test + public void extractingJsonPathStringValueForEmptyString() throws Exception { + assertThat(forJson(TYPES)).extractingJsonPathStringValue("@.emptyString") + .isEmpty(); + } + + @Test + public void extractingJsonPathStringValueForWrongType() throws Exception { + String expression = "$.num"; + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage( + "Expected a string at JSON path \"" + expression + "\" but found: 5"); + assertThat(forJson(TYPES)).extractingJsonPathStringValue(expression); + } + + @Test + public void extractingJsonPathNumberValue() throws Exception { + assertThat(forJson(TYPES)).extractingJsonPathNumberValue("@.num").isEqualTo(5); + } + + @Test + public void extractingJsonPathNumberValueForMissing() throws Exception { + assertThat(forJson(TYPES)).extractingJsonPathNumberValue("@.bogus").isNull(); + } + + @Test + public void extractingJsonPathNumberValueForWrongType() throws Exception { + String expression = "$.str"; + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage( + "Expected a number at JSON path \"" + expression + "\" but found: 'foo'"); + assertThat(forJson(TYPES)).extractingJsonPathNumberValue(expression); + } + + @Test + public void extractingJsonPathBooleanValue() throws Exception { + assertThat(forJson(TYPES)).extractingJsonPathBooleanValue("@.bool").isTrue(); + } + + @Test + public void extractingJsonPathBooleanValueForMissing() throws Exception { + assertThat(forJson(TYPES)).extractingJsonPathBooleanValue("@.bogus").isNull(); + } + + @Test + public void extractingJsonPathBooleanValueForWrongType() throws Exception { + String expression = "$.str"; + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("Expected a boolean at JSON path \"" + expression + + "\" but found: 'foo'"); + assertThat(forJson(TYPES)).extractingJsonPathBooleanValue(expression); + } + + @Test + public void extractingJsonPathArrayValue() throws Exception { + assertThat(forJson(TYPES)).extractingJsonPathArrayValue("@.arr") + .containsExactly(42); + } + + @Test + public void extractingJsonPathArrayValueForMissing() throws Exception { + assertThat(forJson(TYPES)).extractingJsonPathArrayValue("@.bogus").isNull(); + } + + @Test + public void extractingJsonPathArrayValueForEmpty() throws Exception { + assertThat(forJson(TYPES)).extractingJsonPathArrayValue("@.emptyArray").isEmpty(); + } + + @Test + public void extractingJsonPathArrayValueForWrongType() throws Exception { + String expression = "$.str"; + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage( + "Expected an array at JSON path \"" + expression + "\" but found: 'foo'"); + assertThat(forJson(TYPES)).extractingJsonPathArrayValue(expression); + } + + @Test + public void extractingJsonPathMapValue() throws Exception { + assertThat(forJson(TYPES)).extractingJsonPathMapValue("@.colorMap") + .containsEntry("red", "rojo"); + } + + @Test + public void extractingJsonPathMapValueForMissing() throws Exception { + assertThat(forJson(TYPES)).extractingJsonPathMapValue("@.bogus").isNull(); + } + + @Test + public void extractingJsonPathMapValueForEmpty() throws Exception { + assertThat(forJson(TYPES)).extractingJsonPathMapValue("@.emptyMap").isEmpty(); + } + + @Test + public void extractingJsonPathMapValueForWrongType() throws Exception { + String expression = "$.str"; + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage( + "Expected a map at JSON path \"" + expression + "\" but found: 'foo'"); + assertThat(forJson(TYPES)).extractingJsonPathMapValue(expression); + } + + @Test + public void isNullWhenActualIsNullShouldPass() throws Exception { + assertThat(forJson(null)).isNull(); + } + + private File createFile(String content) throws IOException { + File file = this.tempFolder.newFile("file.json"); + FileCopyUtils.copy(content.getBytes(), file); + return file; + } + + private InputStream createInputStream(String content) throws IOException { + return new ByteArrayInputStream(content.getBytes()); + } + + private Resource createResource(String content) throws IOException { + return new ByteArrayResource(content.getBytes()); + } + + private static String loadJson(String path) { + try { + ClassPathResource resource = new ClassPathResource(path, + JsonContentAssert.class); + return new String(FileCopyUtils.copyToByteArray(resource.getInputStream())); + } + catch (Exception ex) { + throw new IllegalStateException(ex); + } + + } + + private AssertProvider forJson(final String json) { + return new AssertProvider() { + + @Override + public JsonContentAssert assertThat() { + return new JsonContentAssert(JsonContentAssertTests.class, json); + } + + }; + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/json/JsonContentTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/json/JsonContentTests.java new file mode 100644 index 0000000000..f1e8913cff --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/json/JsonContentTests.java @@ -0,0 +1,93 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.boot.test.json; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import org.springframework.core.ResolvableType; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link JsonContent}. + * + * @author Phillip Webb + */ +public class JsonContentTests { + + private static final String JSON = "{\"name\":\"spring\", \"age\":100}"; + + private static final ResolvableType TYPE = ResolvableType + .forClass(ExampleObject.class); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void createWhenResourceLoadClassIsNullShouldThrowException() throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("ResourceLoadClass must not be null"); + new JsonContent(null, TYPE, JSON); + } + + @Test + public void createThenJsonIsNullShouldThrowException() throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("JSON must not be null"); + new JsonContent(getClass(), TYPE, null); + } + + @Test + public void createWhenTypeIsNullShouldCreateContent() throws Exception { + JsonContent content = new JsonContent(getClass(), + null, JSON); + assertThat(content).isNotNull(); + } + + @Test + public void assertThatShouldReturnJsonContentAssert() throws Exception { + JsonContent content = new JsonContent(getClass(), + TYPE, JSON); + assertThat(content.assertThat()).isInstanceOf(JsonContentAssert.class); + } + + @Test + public void getJsonShouldReturnJson() throws Exception { + JsonContent content = new JsonContent(getClass(), + TYPE, JSON); + assertThat(content.getJson()).isEqualTo(JSON); + + } + + @Test + public void toStringWhenHasTypeShouldReturnString() throws Exception { + JsonContent content = new JsonContent(getClass(), + TYPE, JSON); + assertThat(content.toString()) + .isEqualTo("JsonContent " + JSON + " created from " + TYPE); + } + + @Test + public void toStringWhenNoTypeShouldReturnString() throws Exception { + JsonContent content = new JsonContent(getClass(), + null, JSON); + assertThat(content.toString()).isEqualTo("JsonContent " + JSON); + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/json/ObjectContentAssertTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/json/ObjectContentAssertTests.java new file mode 100644 index 0000000000..a6b705ee43 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/json/ObjectContentAssertTests.java @@ -0,0 +1,86 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.boot.test.json; + +import java.util.Collections; +import java.util.Map; + +import org.assertj.core.api.AssertProvider; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ObjectContentAssert}. + * + * @author Phillip Webb + */ +public class ObjectContentAssertTests { + + private static final ExampleObject SOURCE = new ExampleObject(); + + private static final ExampleObject DIFFERENT; + + static { + DIFFERENT = new ExampleObject(); + DIFFERENT.setAge(123); + } + + @Test + public void isEqualToWhenObjectsAreEqualShouldPass() throws Exception { + assertThat(forObject(SOURCE)).isEqualTo(SOURCE); + } + + @Test(expected = AssertionError.class) + public void isEqualToWhenObjectsAreDifferentShouldFail() throws Exception { + assertThat(forObject(SOURCE)).isEqualTo(DIFFERENT); + } + + @Test + public void asArrayForArrayShouldReturnObjectArrayAssert() throws Exception { + ExampleObject[] source = new ExampleObject[] { SOURCE }; + assertThat(forObject(source)).asArray().containsExactly(SOURCE); + } + + @Test(expected = AssertionError.class) + public void asArrayForNonArrayShouldFail() throws Exception { + assertThat(forObject(SOURCE)).asArray(); + } + + @Test + public void asMapForMapShouldReturnMapAssert() throws Exception { + Map source = Collections.singletonMap("a", SOURCE); + assertThat(forObject(source)).asMap().containsEntry("a", SOURCE); + } + + @Test(expected = AssertionError.class) + public void asMapForNonMapShouldFail() throws Exception { + assertThat(forObject(SOURCE)).asMap(); + } + + private AssertProvider> forObject(final Object source) { + return new AssertProvider>() { + + @Override + public ObjectContentAssert assertThat() { + return new ObjectContentAssert(source); + } + + }; + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/json/ObjectContentTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/json/ObjectContentTests.java new file mode 100644 index 0000000000..55ad734980 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/json/ObjectContentTests.java @@ -0,0 +1,85 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.boot.test.json; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import org.springframework.core.ResolvableType; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ObjectContent}. + * + * @author Phillip Webb + */ +public class ObjectContentTests { + + private static final ExampleObject OBJECT = new ExampleObject(); + + private static final ResolvableType TYPE = ResolvableType + .forClass(ExampleObject.class); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void createWhenObjectIsNullShouldThrowException() throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Object must not be null"); + new ObjectContent(TYPE, null); + } + + @Test + public void createWhenTypeIsNullShouldCreateContent() throws Exception { + ObjectContent content = new ObjectContent(null, + OBJECT); + assertThat(content).isNotNull(); + } + + @Test + public void assertThatShouldReturnObjectContentAssert() throws Exception { + ObjectContent content = new ObjectContent(TYPE, + OBJECT); + assertThat(content.assertThat()).isInstanceOf(ObjectContentAssert.class); + } + + @Test + public void getObjectShouldReturnObject() throws Exception { + ObjectContent content = new ObjectContent(TYPE, + OBJECT); + assertThat(content.getObject()).isEqualTo(OBJECT); + } + + @Test + public void toStringWhenHasTypeShouldReturnString() throws Exception { + ObjectContent content = new ObjectContent(TYPE, + OBJECT); + assertThat(content.toString()) + .isEqualTo("ObjectContent " + OBJECT + " created from " + TYPE); + } + + @Test + public void toStringWhenHasNoTypeShouldReturnString() throws Exception { + ObjectContent content = new ObjectContent(null, + OBJECT); + assertThat(content.toString()).isEqualTo("ObjectContent " + OBJECT); + } + +} diff --git a/spring-boot-test/src/test/resources/org/springframework/boot/test/json/different.json b/spring-boot-test/src/test/resources/org/springframework/boot/test/json/different.json new file mode 100644 index 0000000000..99d65c7c17 --- /dev/null +++ b/spring-boot-test/src/test/resources/org/springframework/boot/test/json/different.json @@ -0,0 +1,6 @@ +{ + "gnirps":[ + "boot", + "framework" + ] +} diff --git a/spring-boot-test/src/test/resources/org/springframework/boot/test/json/example.json b/spring-boot-test/src/test/resources/org/springframework/boot/test/json/example.json new file mode 100644 index 0000000000..cdea9c3598 --- /dev/null +++ b/spring-boot-test/src/test/resources/org/springframework/boot/test/json/example.json @@ -0,0 +1,4 @@ +{ + "name" : "Spring", + "age" : 123 +} diff --git a/spring-boot-test/src/test/resources/org/springframework/boot/test/json/lenient-same.json b/spring-boot-test/src/test/resources/org/springframework/boot/test/json/lenient-same.json new file mode 100644 index 0000000000..231865936a --- /dev/null +++ b/spring-boot-test/src/test/resources/org/springframework/boot/test/json/lenient-same.json @@ -0,0 +1,6 @@ +{ + "spring":[ + "framework", + "boot" + ] +} diff --git a/spring-boot-test/src/test/resources/org/springframework/boot/test/json/simpsons.json b/spring-boot-test/src/test/resources/org/springframework/boot/test/json/simpsons.json new file mode 100644 index 0000000000..7e9aad3590 --- /dev/null +++ b/spring-boot-test/src/test/resources/org/springframework/boot/test/json/simpsons.json @@ -0,0 +1,19 @@ +{ + "familyMembers":[ + { + "name":"Homer" + }, + { + "name":"Marge" + }, + { + "name":"Bart" + }, + { + "name":"Lisa" + }, + { + "name":"Maggie" + } + ] +} diff --git a/spring-boot-test/src/test/resources/org/springframework/boot/test/json/source.json b/spring-boot-test/src/test/resources/org/springframework/boot/test/json/source.json new file mode 100644 index 0000000000..93e7f4a96f --- /dev/null +++ b/spring-boot-test/src/test/resources/org/springframework/boot/test/json/source.json @@ -0,0 +1,6 @@ +{ + "spring":[ + "boot", + "framework" + ] +} diff --git a/spring-boot-test/src/test/resources/org/springframework/boot/test/json/types.json b/spring-boot-test/src/test/resources/org/springframework/boot/test/json/types.json new file mode 100644 index 0000000000..c418206984 --- /dev/null +++ b/spring-boot-test/src/test/resources/org/springframework/boot/test/json/types.json @@ -0,0 +1,19 @@ +{ + "str":"foo", + "num":5, + "bool":true, + "arr":[ + 42 + ], + "colorMap":{ + "red":"rojo" + }, + "whitespace":" ", + "emptyString":"", + "emptyArray":[ + + ], + "emptyMap":{ + + } +}