Allow JSON Testers to be `@Autowired`

Switch `@AutoConfigureJsonTesters` to use regular `@Autowired` injection
for JSON testers. Prior to this commit JSON Tester fields were
initialized directly which caused IDE issues and was also a little
confusing.

Fixes gh-6451
This commit is contained in:
Phillip Webb 2016-07-26 19:44:47 -07:00
parent a44cc196de
commit 296dc7132b
13 changed files with 289 additions and 377 deletions

View File

@ -5011,12 +5011,13 @@ annotation.
Spring Boot includes AssertJ based helpers that work with the JSONassert and JsonPath
libraries to check that JSON is as expected. The `JacksonHelper`, `GsonHelper` and
`BasicJsonTester` classes can be used for Jackson, Gson and Strings respectively. Any
helper fields on the test class will be automatically initialized when using `@JsonTest`.
helper fields on the test class can be `@Autowired` when using `@JsonTest`.
[source,java,indent=0]
----
import org.junit.*;
import org.junit.runner.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.autoconfigure.json.*;
import org.springframework.boot.test.context.*;
import org.springframework.boot.test.json.*;
@ -5028,6 +5029,7 @@ helper fields on the test class will be automatically initialized when using `@J
@JsonTest
public class MyJsonTests {
@Autowired
private JacksonTester<VehicleDetails> json;
@Test

View File

@ -19,6 +19,7 @@ package sample.test.service;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.json.JsonTest;
import org.springframework.boot.test.json.JacksonTester;
import org.springframework.test.context.junit4.SpringRunner;
@ -34,6 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat;
@JsonTest
public class VehicleDetailsJsonTests {
@Autowired
private JacksonTester<VehicleDetails> json;
@Test

View File

@ -18,42 +18,34 @@ 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.ImportAutoConfiguration;
import org.springframework.boot.test.autoconfigure.properties.PropertyMapping;
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.
* <p>
* 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
@ImportAutoConfiguration
@PropertyMapping("spring.test.jsontesters")
public @interface AutoConfigureJsonTesters {
/**
* If {@link BasicJsonTester}, {@link JacksonTester} and {@link GsonTester} fields
* should be initialized using marshallers from the {@link ApplicationContext}.
* @return if JSON tester fields should be initialized
* If {@link BasicJsonTester}, {@link JacksonTester} and {@link GsonTester} beans
* should be registered. Defaults to {@code true}
* @return if tester support is enabled
*/
boolean initFields() default true;
boolean enabled() default true;
}

View File

@ -1,174 +0,0 @@
/*
* 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<String, Class<?>> INITIALIZERS;
static {
Map<String, Class<?>> initializers = new LinkedHashMap<String, Class<?>>();
initializers.put("com.fasterxml.jackson.databind.ObjectMapper",
JacksonInitializer.class);
initializers.put("com.google.gson.Gson", GsonInitializer.class);
INITIALIZERS = 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<String, Class<?>> entry : INITIALIZERS.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 <T> void initializeJsonMarshalTesterFields(final TestContext testContext,
final Class<T> marshallerClass, Initializer<T> initializer) {
initializer.initialize(testContext, new ObjectFactory<T>() {
@Override
public T getObject() throws BeansException {
return testContext.getApplicationContext().getBean(marshallerClass);
}
});
}
/**
* Strategy used to initialize JSON testers without cause class not found exceptions.
* @param <M> the marshaller type
*/
interface Initializer<M> {
void initialize(TestContext testContext, ObjectFactory<M> marshaller);
}
/**
* {@link Initializer} for {@link JacksonTester}.
*/
static class JacksonInitializer implements Initializer<ObjectMapper> {
@Override
public void initialize(TestContext testContext,
ObjectFactory<ObjectMapper> marshaller) {
JacksonTester.initFields(testContext.getTestInstance(), marshaller);
}
}
/**
* {@link Initializer} for {@link GsonTester}.
*/
static class GsonInitializer implements Initializer<Gson> {
@Override
public void initialize(TestContext testContext, ObjectFactory<Gson> marshaller) {
GsonTester.initFields(testContext.getTestInstance(), marshaller);
}
}
}

View File

@ -0,0 +1,171 @@
/*
* 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 com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.test.json.AbstractJsonMarshalTester;
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.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.core.ResolvableType;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;
/**
* Auto-configuration for Json testers.
*
* @author Phillip Webb
* @see AutoConfigureJsonTesters
* @since 1.4.0
*/
@Configuration
@ConditionalOnClass(name = "org.assertj.core.api.Assert")
@ConditionalOnProperty("spring.test.jsontesters.enabled")
public class JsonTestersAutoConfiguration {
@Bean
public static JsonMarshalTestersBeanPostProcessor jsonMarshalTestersBeanPostProcessor() {
return new JsonMarshalTestersBeanPostProcessor();
}
@Bean
@Scope("prototype")
public FactoryBean<BasicJsonTester> BasicJsonTesterFactoryBean() {
return new JsonTesterFactoryBean<BasicJsonTester, Void>(BasicJsonTester.class,
null);
}
@Bean
@Scope("prototype")
@ConditionalOnClass(ObjectMapper.class)
@ConditionalOnBean(ObjectMapper.class)
public FactoryBean<JacksonTester<?>> jacksonTesterFactoryBean(ObjectMapper mapper) {
return new JsonTesterFactoryBean<JacksonTester<?>, ObjectMapper>(
JacksonTester.class, mapper);
}
@Bean
@Scope("prototype")
@ConditionalOnClass(ObjectMapper.class)
@ConditionalOnBean(Gson.class)
public FactoryBean<GsonTester<?>> gsonTesterFactoryBean(Gson gson) {
return new JsonTesterFactoryBean<GsonTester<?>, Gson>(GsonTester.class, gson);
}
/**
* {@link FactoryBean} used to create JSON Tester instances.
*/
private class JsonTesterFactoryBean<T, M> implements FactoryBean<T> {
private final Class<?> objectType;
private final M marshaller;
JsonTesterFactoryBean(Class<?> objectType, M marshaller) {
this.objectType = objectType;
this.marshaller = marshaller;
}
@Override
public boolean isSingleton() {
return false;
}
@Override
@SuppressWarnings("unchecked")
public T getObject() throws Exception {
if (this.marshaller == null) {
Constructor<?> constructor = this.objectType.getDeclaredConstructor();
ReflectionUtils.makeAccessible(constructor);
return (T) BeanUtils.instantiateClass(constructor);
}
Constructor<?>[] constructors = this.objectType.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
if (constructor.getParameterTypes().length == 1
&& constructor.getParameterTypes()[0]
.isInstance(this.marshaller)) {
ReflectionUtils.makeAccessible(constructor);
return (T) BeanUtils.instantiateClass(constructor, this.marshaller);
}
}
throw new IllegalStateException(
this.objectType + " does not have a usable constructor");
}
@Override
public Class<?> getObjectType() {
return this.objectType;
}
}
/**
* {@link BeanPostProcessor} used to initialize JSON testers.
*/
private static class JsonMarshalTestersBeanPostProcessor
extends InstantiationAwareBeanPostProcessorAdapter {
@Override
public Object postProcessAfterInitialization(final Object bean, String beanName)
throws BeansException {
ReflectionUtils.doWithFields(bean.getClass(), new FieldCallback() {
@Override
public void doWith(Field field)
throws IllegalArgumentException, IllegalAccessException {
processFiled(bean, field);
}
});
return bean;
}
private void processFiled(Object bean, Field field) {
if (AbstractJsonMarshalTester.class.isAssignableFrom(field.getType())
|| BasicJsonTester.class.isAssignableFrom(field.getType())) {
ResolvableType type = ResolvableType.forField(field).getGeneric();
ReflectionUtils.makeAccessible(field);
Object tester = ReflectionUtils.getField(field, bean);
if (tester != null) {
ReflectionTestUtils.invokeMethod(tester, "initialize",
bean.getClass(), type);
}
}
}
}
}

View File

@ -17,6 +17,10 @@ org.springframework.boot.test.autoconfigure.json.AutoConfigureJson=\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
# AutoConfigureJsonTesters auto-configuration imports
org.springframework.boot.test.autoconfigure.json.AutoConfigureJsonTesters=\
org.springframework.boot.test.autoconfigure.json.JsonTestersAutoConfiguration
# AutoConfigureMockMvc auto-configuration imports
org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc=\
org.springframework.boot.test.autoconfigure.web.servlet.MockMvcAutoConfiguration,\
@ -69,6 +73,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.json.JsonTesterInitializationTestExecutionListener,\
org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener,\
org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener

View File

@ -19,6 +19,7 @@ package org.springframework.boot.test.autoconfigure.json;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.json.BasicJsonTester;
import org.springframework.boot.test.json.GsonTester;
import org.springframework.boot.test.json.JacksonTester;
@ -35,12 +36,16 @@ import static org.assertj.core.api.Assertions.assertThat;
@JsonTest
public class JsonTestIntegrationTests {
@Autowired
private BasicJsonTester basicJson;
@Autowired
private JacksonTester<ExampleBasicObject> jacksonBasicJson;
@Autowired
private JacksonTester<ExampleCustomObject> jacksonCustomJson;
@Autowired
private GsonTester<ExampleBasicObject> gsonJson;
@Test

View File

@ -19,6 +19,7 @@ package org.springframework.boot.test.autoconfigure.json;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.json.BasicJsonTester;
import org.springframework.boot.test.json.GsonTester;
import org.springframework.boot.test.json.JacksonTester;
@ -33,13 +34,16 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
@RunWith(SpringRunner.class)
@JsonTest
@AutoConfigureJsonTesters(initFields = false)
@AutoConfigureJsonTesters(enabled = false)
public class JsonTestWithAutoConfigureJsonTestersTests {
@Autowired(required = false)
private BasicJsonTester basicJson;
@Autowired(required = false)
private JacksonTester<ExampleBasicObject> jacksonTester;
@Autowired(required = false)
private GsonTester<ExampleBasicObject> gsonTester;
@Test

View File

@ -1,179 +0,0 @@
/*
* 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<Object> tester;
}
@AutoConfigureJsonTesters
static class WithGsonTester {
private GsonTester<Object> tester;
}
@AutoConfigureJsonTesters(initFields = false)
static class WithInitFieldsFalse {
private BasicJsonTester basicTester;
private JacksonTester<Object> jacksonTester;
private GsonTester<Object> gsonTester;
}
@AutoConfigureJsonTesters(initFields = true)
static class WithInitFieldsTrue {
private BasicJsonTester tester;
}
static class WithoutAnnotation {
private BasicJsonTester basicTester;
private JacksonTester<Object> jacksonTester;
private GsonTester<Object> gsonTester;
}
}

View File

@ -70,9 +70,15 @@ import org.springframework.util.ReflectionUtils.FieldCallback;
*/
public abstract class AbstractJsonMarshalTester<T> {
private final Class<?> resourceLoadClass;
private Class<?> resourceLoadClass;
private final ResolvableType type;
private ResolvableType type;
/**
* Create a new uninitialized {@link AbstractJsonMarshalTester} instance.
*/
protected AbstractJsonMarshalTester() {
}
/**
* Create a new {@link AbstractJsonMarshalTester} instance.
@ -83,8 +89,20 @@ public abstract class AbstractJsonMarshalTester<T> {
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;
initialize(resourceLoadClass, type);
}
/**
* Initialize the marshal tester for use.
* @param resourceLoadClass the source class used when loading relative classpath
* resources
* @param type the type under test
*/
protected final void initialize(Class<?> resourceLoadClass, ResolvableType type) {
if (this.resourceLoadClass == null && this.type == null) {
this.resourceLoadClass = resourceLoadClass;
this.type = type;
}
}
/**
@ -102,6 +120,7 @@ public abstract class AbstractJsonMarshalTester<T> {
* @throws IOException on write error
*/
public JsonContent<T> write(T value) throws IOException {
verify();
Assert.notNull(value, "Value must not be null");
String json = writeObject(value, this.type);
return new JsonContent<T>(this.resourceLoadClass, this.type, json);
@ -114,6 +133,7 @@ public abstract class AbstractJsonMarshalTester<T> {
* @throws IOException on parse error
*/
public T parseObject(byte[] jsonBytes) throws IOException {
verify();
return parse(jsonBytes).getObject();
}
@ -124,6 +144,7 @@ public abstract class AbstractJsonMarshalTester<T> {
* @throws IOException on parse error
*/
public ObjectContent<T> parse(byte[] jsonBytes) throws IOException {
verify();
Assert.notNull(jsonBytes, "JsonBytes must not be null");
return read(new ByteArrayResource(jsonBytes));
}
@ -135,6 +156,7 @@ public abstract class AbstractJsonMarshalTester<T> {
* @throws IOException on parse error
*/
public T parseObject(String jsonString) throws IOException {
verify();
return parse(jsonString).getObject();
}
@ -145,6 +167,7 @@ public abstract class AbstractJsonMarshalTester<T> {
* @throws IOException on parse error
*/
public ObjectContent<T> parse(String jsonString) throws IOException {
verify();
Assert.notNull(jsonString, "JsonString must not be null");
return read(new StringReader(jsonString));
}
@ -157,6 +180,7 @@ public abstract class AbstractJsonMarshalTester<T> {
* @throws IOException on read error
*/
public T readObject(String resourcePath) throws IOException {
verify();
return read(resourcePath).getObject();
}
@ -168,6 +192,7 @@ public abstract class AbstractJsonMarshalTester<T> {
* @throws IOException on read error
*/
public ObjectContent<T> read(String resourcePath) throws IOException {
verify();
Assert.notNull(resourcePath, "ResourcePath must not be null");
return read(new ClassPathResource(resourcePath, this.resourceLoadClass));
}
@ -179,6 +204,7 @@ public abstract class AbstractJsonMarshalTester<T> {
* @throws IOException on read error
*/
public T readObject(File file) throws IOException {
verify();
return read(file).getObject();
}
@ -189,6 +215,7 @@ public abstract class AbstractJsonMarshalTester<T> {
* @throws IOException on read error
*/
public ObjectContent<T> read(File file) throws IOException {
verify();
Assert.notNull(file, "File must not be null");
return read(new FileSystemResource(file));
}
@ -200,6 +227,7 @@ public abstract class AbstractJsonMarshalTester<T> {
* @throws IOException on read error
*/
public T readObject(InputStream inputStream) throws IOException {
verify();
return read(inputStream).getObject();
}
@ -210,6 +238,7 @@ public abstract class AbstractJsonMarshalTester<T> {
* @throws IOException on read error
*/
public ObjectContent<T> read(InputStream inputStream) throws IOException {
verify();
Assert.notNull(inputStream, "InputStream must not be null");
return read(new InputStreamResource(inputStream));
}
@ -221,6 +250,7 @@ public abstract class AbstractJsonMarshalTester<T> {
* @throws IOException on read error
*/
public T readObject(Resource resource) throws IOException {
verify();
return read(resource).getObject();
}
@ -231,6 +261,7 @@ public abstract class AbstractJsonMarshalTester<T> {
* @throws IOException on read error
*/
public ObjectContent<T> read(Resource resource) throws IOException {
verify();
Assert.notNull(resource, "Resource must not be null");
InputStream inputStream = resource.getInputStream();
T object = readObject(inputStream, this.type);
@ -245,6 +276,7 @@ public abstract class AbstractJsonMarshalTester<T> {
* @throws IOException on read error
*/
public T readObject(Reader reader) throws IOException {
verify();
return read(reader).getObject();
}
@ -255,6 +287,7 @@ public abstract class AbstractJsonMarshalTester<T> {
* @throws IOException on read error
*/
public ObjectContent<T> read(Reader reader) throws IOException {
verify();
Assert.notNull(reader, "Reader must not be null");
T object = readObject(reader, this.type);
closeQuietly(reader);
@ -269,6 +302,12 @@ public abstract class AbstractJsonMarshalTester<T> {
}
}
private void verify() {
Assert.state(this.resourceLoadClass != null,
"Unitialized JsonMarshalTester (ResourceLoadClass is null)");
Assert.state(this.type != null, "Unitialized JsonMarshalTester (Type is null)");
}
/**
* Write the specified object to a JSON string.
* @param value the source value (never {@code null})

View File

@ -19,6 +19,7 @@ package org.springframework.boot.test.json;
import java.io.File;
import java.io.InputStream;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
@ -45,7 +46,13 @@ import org.springframework.util.Assert;
*/
public class BasicJsonTester {
private final JsonLoader loader;
private JsonLoader loader;
/**
* Create a new uninialized {@link BasicJsonTester} instance.
*/
protected BasicJsonTester() {
}
/**
* Create a new {@link BasicJsonTester} instance.
@ -56,6 +63,18 @@ public class BasicJsonTester {
this.loader = new JsonLoader(resourceLoadClass);
}
/**
* Initialize the marshal tester for use.
* @param resourceLoadClass the source class used when loading relative classpath
* resources
* @param type the type under test
*/
protected final void initialize(Class<?> resourceLoadClass, ResolvableType type) {
if (this.loader == 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
@ -64,6 +83,7 @@ public class BasicJsonTester {
* @return the JSON content
*/
public JsonContent<Object> from(CharSequence source) {
verify();
return getJsonContent(this.loader.getJson(source));
}
@ -74,6 +94,7 @@ public class BasicJsonTester {
* @return the JSON content
*/
public JsonContent<Object> from(String path, Class<?> resourceLoadClass) {
verify();
return getJsonContent(this.loader.getJson(path, resourceLoadClass));
}
@ -83,6 +104,7 @@ public class BasicJsonTester {
* @return the JSON content
*/
public JsonContent<Object> from(byte[] source) {
verify();
return getJsonContent(this.loader.getJson(source));
}
@ -92,6 +114,7 @@ public class BasicJsonTester {
* @return the JSON content
*/
public JsonContent<Object> from(File source) {
verify();
return getJsonContent(this.loader.getJson(source));
}
@ -101,6 +124,7 @@ public class BasicJsonTester {
* @return the JSON content
*/
public JsonContent<Object> from(InputStream source) {
verify();
return getJsonContent(this.loader.getJson(source));
}
@ -110,9 +134,14 @@ public class BasicJsonTester {
* @return the JSON content
*/
public JsonContent<Object> from(Resource source) {
verify();
return getJsonContent(this.loader.getJson(source));
}
private void verify() {
Assert.state(this.loader != null, "Unitialized BasicJsonTester");
}
private JsonContent<Object> getJsonContent(String json) {
return new JsonContent<Object>(this.loader.getResourceLoadClass(), null, json);
}

View File

@ -57,6 +57,15 @@ public class GsonTester<T> extends AbstractJsonMarshalTester<T> {
private final Gson gson;
/**
* Create a new uninitialized {@link GsonTester} instance.
* @param gson the Gson instance
*/
protected GsonTester(Gson gson) {
Assert.notNull(gson, "Gson must not be null");
this.gson = gson;
}
/**
* Create a new {@link GsonTester} instance.
* @param resourceLoadClass the source class used to load resources

View File

@ -59,6 +59,15 @@ public class JacksonTester<T> extends AbstractJsonMarshalTester<T> {
private final ObjectMapper objectMapper;
/**
* Create a new {@link JacksonTester} instance.
* @param objectMapper the Jackson object mapper
*/
protected JacksonTester(ObjectMapper objectMapper) {
Assert.notNull(objectMapper, "ObjectMapper must not be null");
this.objectMapper = objectMapper;
}
/**
* Create a new {@link JacksonTester} instance.
* @param resourceLoadClass the source class used to load resources