diff --git a/spring-boot-test-autoconfigure/pom.xml b/spring-boot-test-autoconfigure/pom.xml index 642446151e..55c810819f 100644 --- a/spring-boot-test-autoconfigure/pom.xml +++ b/spring-boot-test-autoconfigure/pom.xml @@ -29,6 +29,11 @@ spring-boot-autoconfigure + + com.fasterxml.jackson.core + jackson-databind + true + org.springframework spring-test diff --git a/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/AutoConfigureJsonTesters.java b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/AutoConfigureJsonTesters.java new file mode 100644 index 0000000000..9da8de5209 --- /dev/null +++ b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/AutoConfigureJsonTesters.java @@ -0,0 +1,58 @@ +/* + * 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.autoconfigure.json; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.test.json.BasicJsonTester; +import org.springframework.boot.test.json.GsonTester; +import org.springframework.boot.test.json.JacksonTester; +import org.springframework.context.ApplicationContext; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.TestExecutionListeners.MergeMode; + +/** + * Annotation that can be applied to a test class to enable and configure + * auto-configuration of JSON Testers. + *

+ * NOTE: {@code @AutoConfigureJsonTesters} works in conjunction with + * {@link JsonTesterInitializationTestExecutionListener}. If you declare your own + * {@link TestExecutionListeners @TestExecutionListeners} and don't + * {@link MergeMode#MERGE_WITH_DEFAULTS merge with defaults} you must include + * {@link JsonTesterInitializationTestExecutionListener} to use this annotation. + * + * @author Phillip Webb + * @see JsonTesterInitializationTestExecutionListener + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface AutoConfigureJsonTesters { + + /** + * If {@link BasicJsonTester}, {@link JacksonTester} and {@link GsonTester} fields + * should be initialized using marshallers from the {@link ApplicationContext}. + */ + boolean initFields() default true; + +} diff --git a/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/ImportJsonAutoConfiguration.java b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/ImportJsonAutoConfiguration.java new file mode 100644 index 0000000000..7863d6c679 --- /dev/null +++ b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/ImportJsonAutoConfiguration.java @@ -0,0 +1,44 @@ +/* + * 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.autoconfigure.json; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration; +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; + +/** + * {@link ImportAutoConfiguration Auto-configuration imports} for typical JSON tests. Most + * tests should consider using {@link JsonTest @JsonTest} rather than using this + * annotation directly. + * + * @author Phillip Webb + * @since 1.4.0 + * @see JsonTest + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@ImportAutoConfiguration({ GsonAutoConfiguration.class, JacksonAutoConfiguration.class }) +public @interface ImportJsonAutoConfiguration { + +} diff --git a/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/JsonExcludeFilter.java b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/JsonExcludeFilter.java new file mode 100644 index 0000000000..0e5a73efe0 --- /dev/null +++ b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/JsonExcludeFilter.java @@ -0,0 +1,80 @@ +/* + * 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.autoconfigure.json; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +import com.fasterxml.jackson.databind.Module; + +import org.springframework.boot.context.TypeExcludeFilter; +import org.springframework.boot.jackson.JsonComponent; +import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.core.annotation.AnnotatedElementUtils; + +/** + * {@link TypeExcludeFilter} for {@link JsonTest @JsonTest}. + * + * @author Phillip Webb + */ +class JsonExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { + + private static final Set> DEFAULT_INCLUDES; + + static { + Set> includes = new LinkedHashSet>(); + includes.add(Module.class); + includes.add(JsonComponent.class); + DEFAULT_INCLUDES = Collections.unmodifiableSet(includes); + }; + + private final JsonTest annotation; + + JsonExcludeFilter(Class testClass) { + this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, + JsonTest.class); + } + + @Override + protected boolean hasAnnotation() { + return this.annotation != null; + } + + @Override + protected Filter[] getFilters(FilterType type) { + switch (type) { + case INCLUDE: + return this.annotation.includeFilters(); + case EXCLUDE: + return this.annotation.excludeFilters(); + } + throw new IllegalStateException("Unsupported type " + type); + } + + @Override + protected boolean isUseDefaultFilters() { + return this.annotation.useDefaultFilters(); + } + + @Override + protected Set> getDefaultIncudes() { + return DEFAULT_INCLUDES; + } + +} diff --git a/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/JsonTest.java b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/JsonTest.java new file mode 100644 index 0000000000..6f0620e262 --- /dev/null +++ b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/JsonTest.java @@ -0,0 +1,64 @@ +/* + * 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.autoconfigure.json; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration; +import org.springframework.boot.test.autoconfigure.filter.TypeExcludeFilters; +import org.springframework.boot.test.context.SpringBootTestContextBootstrapper; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.test.context.BootstrapWith; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@BootstrapWith(SpringBootTestContextBootstrapper.class) +@OverrideAutoConfiguration(enabled = false) +@TypeExcludeFilters(JsonExcludeFilter.class) +@ImportJsonAutoConfiguration +@AutoConfigureJsonTesters +public @interface JsonTest { + + /** + * Determines if default filtering should be used with + * {@link SpringBootApplication @SpringBootApplication}. By default only + * {@code @JsonComponent} and {@code Module} beans are included. + * @see #includeFilters() + * @see #excludeFilters() + */ + boolean useDefaultFilters() default true; + + /** + * A set of include filters which can be used to add otherwise filtered beans to the + * application context. + */ + Filter[] includeFilters() default {}; + + /** + * A set of exclude filters which can be used to filter beans that would otherwise be + * added to the application context. + */ + Filter[] excludeFilters() default {}; +} diff --git a/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/JsonTesterInitializationTestExecutionListener.java b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/JsonTesterInitializationTestExecutionListener.java new file mode 100644 index 0000000000..26d9eb8338 --- /dev/null +++ b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/JsonTesterInitializationTestExecutionListener.java @@ -0,0 +1,174 @@ +/* + * 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.autoconfigure.json; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.boot.test.json.BasicJsonTester; +import org.springframework.boot.test.json.GsonTester; +import org.springframework.boot.test.json.JacksonTester; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.test.context.TestContext; +import org.springframework.test.context.TestExecutionListener; +import org.springframework.test.context.support.AbstractTestExecutionListener; +import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.ReflectionUtils.FieldCallback; + +/** + * {@link TestExecutionListener} to initialize JSON tester fields. + * + * @author Phillip Webb + * @since 1.4.0 + */ +public class JsonTesterInitializationTestExecutionListener + extends AbstractTestExecutionListener { + + private static final String ASSERTJ_CLASS = "org.assertj.core.api.Assert"; + + private static final Map> INITALIZERS; + + static { + Map> initializers = new LinkedHashMap>(); + initializers.put("com.fasterxml.jackson.databind.ObjectMapper", + JacksonInitializer.class); + initializers.put("com.google.gson.Gson", GsonInitializer.class); + INITALIZERS = Collections.unmodifiableMap(initializers); + } + + @Override + public void prepareTestInstance(TestContext testContext) throws Exception { + ClassLoader classLoader = getClass().getClassLoader(); + if (ClassUtils.isPresent(ASSERTJ_CLASS, classLoader) + && shouldInitializeFields(testContext)) { + initializeBasicJsonTesterFields(testContext); + initializeJsonMarshalTesterFields(classLoader, testContext); + } + } + + private boolean shouldInitializeFields(TestContext testContext) { + AutoConfigureJsonTesters annotation = AnnotatedElementUtils.getMergedAnnotation( + testContext.getTestClass(), AutoConfigureJsonTesters.class); + return (annotation != null && annotation.initFields()); + } + + private void initializeBasicJsonTesterFields(final TestContext testContext) { + ReflectionUtils.doWithFields(testContext.getTestClass(), new FieldCallback() { + + @Override + public void doWith(Field field) + throws IllegalArgumentException, IllegalAccessException { + if (BasicJsonTester.class.isAssignableFrom(field.getType())) { + setupField(field); + } + } + + private void setupField(Field field) { + ReflectionUtils.makeAccessible(field); + Object existingInstance = ReflectionUtils.getField(field, + testContext.getTestInstance()); + if (existingInstance == null) { + ReflectionUtils.setField(field, testContext.getTestInstance(), + new BasicJsonTester(testContext.getTestClass())); + } + } + + }); + } + + private void initializeJsonMarshalTesterFields(ClassLoader classLoader, + TestContext testContext) { + for (Map.Entry> entry : INITALIZERS.entrySet()) { + if (ClassUtils.isPresent(entry.getKey(), classLoader)) { + initializeJsonMarshalTesterFields(classLoader, testContext, + entry.getKey(), entry.getValue()); + } + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private void initializeJsonMarshalTesterFields(ClassLoader classLoader, + TestContext testContext, String marshallerClassName, Class initializer) { + try { + Constructor constructor = initializer.getDeclaredConstructor(); + ReflectionUtils.makeAccessible(constructor); + initializeJsonMarshalTesterFields(testContext, + ClassUtils.resolveClassName(marshallerClassName, classLoader), + (Initializer) constructor.newInstance()); + } + catch (Throwable ex) { + throw new IllegalStateException(ex); + } + } + + private void initializeJsonMarshalTesterFields(final TestContext testContext, + final Class marshallerClass, Initializer initializer) { + initializer.initialize(testContext, new ObjectFactory() { + + @Override + public T getObject() throws BeansException { + return testContext.getApplicationContext().getBean(marshallerClass); + } + + }); + } + + /** + * Strategy used to initialize JSON testers without cause class not found exceptions. + * @param the marshaller type + */ + interface Initializer { + + void initialize(TestContext testContext, ObjectFactory marshaller); + + } + + /** + * {@link Initializer} for {@link JacksonTester}. + */ + static class JacksonInitializer implements Initializer { + + @Override + public void initialize(TestContext testContext, + ObjectFactory marshaller) { + JacksonTester.initFields(testContext.getTestInstance(), marshaller); + } + + } + + /** + * {@link Initializer} for {@link GsonTester}. + */ + static class GsonInitializer implements Initializer { + + @Override + public void initialize(TestContext testContext, ObjectFactory marshaller) { + GsonTester.initFields(testContext.getTestInstance(), marshaller); + } + + } + +} diff --git a/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/package-info.java b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/json/package-info.java new file mode 100644 index 0000000000..5e7b1bf37c --- /dev/null +++ b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/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. + */ + +/** + * Auto-configuration for JSON tests. + */ +package org.springframework.boot.test.autoconfigure.json; diff --git a/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories index 9689cc16ac..136c97df4e 100644 --- a/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories @@ -6,4 +6,5 @@ org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCus # Test Execution Listeners org.springframework.test.context.TestExecutionListener=\ -org.springframework.boot.test.autoconfigure.AutoConfigureReportTestExecutionListener +org.springframework.boot.test.autoconfigure.AutoConfigureReportTestExecutionListener,\ +org.springframework.boot.test.autoconfigure.json.JsonTesterInitializationTestExecutionListener diff --git a/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/ExampleBasicObject.java b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/ExampleBasicObject.java new file mode 100644 index 0000000000..c2f337ca29 --- /dev/null +++ b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/ExampleBasicObject.java @@ -0,0 +1,49 @@ +/* + * 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.autoconfigure.json; + +/** + * Example object to read/write as JSON. + * + * @author Phillip Webb + */ +public class ExampleBasicObject { + + private String value; + + public String getValue() { + return this.value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public int hashCode() { + return this.value.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && obj.getClass().equals(getClass())) { + return this.value.equals(((ExampleBasicObject) obj).value); + } + return false; + } + +} diff --git a/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/ExampleCustomObject.java b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/ExampleCustomObject.java new file mode 100644 index 0000000000..5cef2f416b --- /dev/null +++ b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/ExampleCustomObject.java @@ -0,0 +1,50 @@ +/* + * 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.autoconfigure.json; + +/** + * Example object to read/write as JSON via {@link ExampleJsonComponent}. + * + * @author Phillip Webb + */ +public class ExampleCustomObject { + + private String value; + + public ExampleCustomObject(String value) { + this.value = value; + } + + @Override + public int hashCode() { + return this.value.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && obj.getClass().equals(getClass())) { + return this.value.equals(((ExampleCustomObject) obj).value); + } + return false; + } + + @Override + public String toString() { + return this.value; + } + +} diff --git a/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/ExampleJsonApplication.java b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/ExampleJsonApplication.java new file mode 100644 index 0000000000..2161ea5811 --- /dev/null +++ b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/ExampleJsonApplication.java @@ -0,0 +1,30 @@ +/* + * 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.autoconfigure.json; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Example {@link SpringBootApplication @SpringBootApplication} for use with + * {@link JsonTest @JsonTest} tests. + * + * @author Phillip Webb + */ +@SpringBootApplication +public class ExampleJsonApplication { + +} diff --git a/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/ExampleJsonComponent.java b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/ExampleJsonComponent.java new file mode 100644 index 0000000000..8813e4b031 --- /dev/null +++ b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/ExampleJsonComponent.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.autoconfigure.json; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializerProvider; + +import org.springframework.boot.jackson.JsonComponent; +import org.springframework.boot.jackson.JsonObjectDeserializer; +import org.springframework.boot.jackson.JsonObjectSerializer; + +/** + * Example {@link JsonComponent} for use with {@link JsonTest @JsonTest} tests. + * + * @author Phillip Webb + */ +@JsonComponent +public class ExampleJsonComponent { + + public static class Serializer extends JsonObjectSerializer { + + @Override + protected void serializeObject(ExampleCustomObject value, JsonGenerator jgen, + SerializerProvider provider) throws IOException { + jgen.writeStringField("value", value.toString()); + } + + } + + public static class Deserialzer extends JsonObjectDeserializer { + + @Override + protected ExampleCustomObject deserializeObject(JsonParser jasonParser, + DeserializationContext context, ObjectCodec codec, JsonNode tree) + throws IOException { + return new ExampleCustomObject( + nullSafeValue(tree.get("value"), String.class)); + } + + } + +} diff --git a/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/JsonTestIntegrationTests.java b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/JsonTestIntegrationTests.java new file mode 100644 index 0000000000..0b165abace --- /dev/null +++ b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/JsonTestIntegrationTests.java @@ -0,0 +1,71 @@ +/* + * 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.autoconfigure.json; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.boot.test.json.BasicJsonTester; +import org.springframework.boot.test.json.GsonTester; +import org.springframework.boot.test.json.JacksonTester; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link JsonTest}. + * + * @author Phillip Webb + */ +@RunWith(SpringRunner.class) +@JsonTest +public class JsonTestIntegrationTests { + + private BasicJsonTester basicJson; + + private JacksonTester jacksonBasicJson; + + private JacksonTester jacksonCustomJson; + + private GsonTester gsonJson; + + @Test + public void basicJson() throws Exception { + assertThat(this.basicJson.from("{\"a\":\"b\"}")).hasJsonPathStringValue("@.a"); + } + + @Test + public void jacksonBasic() throws Exception { + ExampleBasicObject object = new ExampleBasicObject(); + object.setValue("spring"); + assertThat(this.jacksonBasicJson.write(object)).isEqualToJson("example.json"); + } + + @Test + public void jacksonCustom() throws Exception { + ExampleCustomObject object = new ExampleCustomObject("spring"); + assertThat(this.jacksonCustomJson.write(object)).isEqualToJson("example.json"); + } + + @Test + public void gson() throws Exception { + ExampleBasicObject object = new ExampleBasicObject(); + object.setValue("spring"); + assertThat(this.gsonJson.write(object)).isEqualToJson("example.json"); + } + +} diff --git a/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/JsonTestWithAutoConfigureJsonTestersTests.java b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/JsonTestWithAutoConfigureJsonTestersTests.java new file mode 100644 index 0000000000..4ecd1a99ff --- /dev/null +++ b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/JsonTestWithAutoConfigureJsonTestersTests.java @@ -0,0 +1,60 @@ +/* + * 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.autoconfigure.json; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.boot.test.json.BasicJsonTester; +import org.springframework.boot.test.json.GsonTester; +import org.springframework.boot.test.json.JacksonTester; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link JsonTest} with {@link AutoConfigureJsonTesters}. + * + * @author Phillip Webb + */ +@RunWith(SpringRunner.class) +@JsonTest +@AutoConfigureJsonTesters(initFields = false) +public class JsonTestWithAutoConfigureJsonTestersTests { + + private BasicJsonTester basicJson; + + private JacksonTester jacksonTester; + + private GsonTester gsonTester; + + @Test + public void basicJson() throws Exception { + assertThat(this.basicJson).isNull(); + } + + @Test + public void jackson() throws Exception { + assertThat(this.jacksonTester).isNull(); + } + + @Test + public void gson() throws Exception { + assertThat(this.gsonTester).isNull(); + } + +} diff --git a/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/JsonTesterInitializationTestExecutionListenerTests.java b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/JsonTesterInitializationTestExecutionListenerTests.java new file mode 100644 index 0000000000..c568ffdcc7 --- /dev/null +++ b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/JsonTesterInitializationTestExecutionListenerTests.java @@ -0,0 +1,179 @@ +/* + * 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.autoconfigure.json; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.GsonBuilder; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import org.springframework.boot.test.json.BasicJsonTester; +import org.springframework.boot.test.json.GsonTester; +import org.springframework.boot.test.json.JacksonTester; +import org.springframework.context.support.StaticApplicationContext; +import org.springframework.test.context.TestContext; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link JsonTesterInitializationTestExecutionListener}. + * + * @author Phillip Webb + */ +public class JsonTesterInitializationTestExecutionListenerTests { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private JsonTesterInitializationTestExecutionListener listener = new JsonTesterInitializationTestExecutionListener(); + + @Test + public void prepareTestContextShouldInitializeBasicJsonTester() throws Exception { + WithBasicJsonTester instance = new WithBasicJsonTester(); + this.listener.prepareTestInstance(mockTestContext(instance)); + assertThat(instance.tester).isNotNull(); + } + + @Test + public void prepareTestContextShouldInitializeJacksonTester() throws Exception { + WithJacksonTester instance = new WithJacksonTester(); + this.listener.prepareTestInstance(mockTestContext(instance, new ObjectMapper())); + assertThat(instance.tester).isNotNull(); + } + + @Test + public void prepareTestContextShouldInitializeGsonTester() throws Exception { + WithGsonTester instance = new WithGsonTester(); + this.listener.prepareTestInstance( + mockTestContext(instance, new GsonBuilder().create())); + assertThat(instance.tester).isNotNull(); + } + + @Test + public void prepareTestContextWhenInitFieldsFalseShouldNotInitializeTesters() + throws Exception { + WithInitFieldsFalse instance = new WithInitFieldsFalse(); + this.listener.prepareTestInstance(mockTestContext(instance, new ObjectMapper())); + assertThat(instance.basicTester).isNull(); + assertThat(instance.jacksonTester).isNull(); + assertThat(instance.gsonTester).isNull(); + } + + @Test + public void prepareTestContextWhenInitFieldsTrueShouldInitializeTesters() + throws Exception { + WithInitFieldsTrue instance = new WithInitFieldsTrue(); + this.listener.prepareTestInstance(mockTestContext(instance)); + assertThat(instance.tester).isNotNull(); + } + + @Test + public void prepareTestContextWhenMissingAnnotationShouldNotInitializeTesters() + throws Exception { + WithoutAnnotation instance = new WithoutAnnotation(); + this.listener.prepareTestInstance(mockTestContext(instance)); + assertThat(instance.basicTester).isNull(); + assertThat(instance.jacksonTester).isNull(); + assertThat(instance.gsonTester).isNull(); + } + + @Test + public void prepareTestContextWhenHasJacksonTesterButNoObjectMapperBeanShouldThrowException() + throws Exception { + WithJacksonTester instance = new WithJacksonTester(); + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage("ObjectMapper"); + this.listener.prepareTestInstance(mockTestContext(instance)); + } + + @Test + public void prepareTestContextWhenHasJacksonTesterButNoGsonBeanShouldThrowException() + throws Exception { + WithGsonTester instance = new WithGsonTester(); + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage("Gson"); + this.listener.prepareTestInstance(mockTestContext(instance)); + } + + private TestContext mockTestContext(Object testInstance) { + return mockTestContext(testInstance, null); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private TestContext mockTestContext(Object testInstance, Object bean) { + TestContext testContext = mock(TestContext.class); + StaticApplicationContext applicationContext = new StaticApplicationContext(); + if (bean != null) { + applicationContext.getBeanFactory().registerSingleton("bean", bean); + } + given(testContext.getApplicationContext()).willReturn(applicationContext); + given(testContext.getTestClass()).willReturn((Class) testInstance.getClass()); + given(testContext.getTestInstance()).willReturn(testInstance); + return testContext; + } + + @AutoConfigureJsonTesters + static class WithBasicJsonTester { + + private BasicJsonTester tester; + + } + + @AutoConfigureJsonTesters + static class WithJacksonTester { + + private JacksonTester tester; + } + + @AutoConfigureJsonTesters + static class WithGsonTester { + + private GsonTester tester; + } + + @AutoConfigureJsonTesters(initFields = false) + static class WithInitFieldsFalse { + + private BasicJsonTester basicTester; + + private JacksonTester jacksonTester; + + private GsonTester gsonTester; + + } + + @AutoConfigureJsonTesters(initFields = true) + static class WithInitFieldsTrue { + + private BasicJsonTester tester; + + } + + static class WithoutAnnotation { + + private BasicJsonTester basicTester; + + private JacksonTester jacksonTester; + + private GsonTester gsonTester; + + } + +} diff --git a/spring-boot-test-autoconfigure/src/test/resources/org/springframework/boot/test/autoconfigure/json/example.json b/spring-boot-test-autoconfigure/src/test/resources/org/springframework/boot/test/autoconfigure/json/example.json new file mode 100644 index 0000000000..40cc01ff6f --- /dev/null +++ b/spring-boot-test-autoconfigure/src/test/resources/org/springframework/boot/test/autoconfigure/json/example.json @@ -0,0 +1,3 @@ +{ + "value" : "spring" +}