001/* 002 * Copyright 2012-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.test.json; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.io.Reader; 022 023import com.fasterxml.jackson.databind.JavaType; 024import com.fasterxml.jackson.databind.ObjectMapper; 025import com.fasterxml.jackson.databind.ObjectReader; 026import com.fasterxml.jackson.databind.ObjectWriter; 027 028import org.springframework.beans.factory.ObjectFactory; 029import org.springframework.core.ResolvableType; 030import org.springframework.util.Assert; 031 032/** 033 * AssertJ based JSON tester backed by Jackson. Usually instantiated via 034 * {@link #initFields(Object, ObjectMapper)}, for example: <pre class="code"> 035 * public class ExampleObjectJsonTests { 036 * 037 * private JacksonTester<ExampleObject> json; 038 * 039 * @Before 040 * public void setup() { 041 * ObjectMapper objectMapper = new ObjectMapper(); 042 * JacksonTester.initFields(this, objectMapper); 043 * } 044 * 045 * @Test 046 * public void testWriteJson() throws IOException { 047 * ExampleObject object = //... 048 * assertThat(json.write(object)).isEqualToJson("expected.json"); 049 * } 050 * 051 * } 052 * </pre> 053 * 054 * See {@link AbstractJsonMarshalTester} for more details. 055 * 056 * @param <T> the type under test 057 * @author Phillip Webb 058 * @author Madhura Bhave 059 * @since 1.4.0 060 */ 061public class JacksonTester<T> extends AbstractJsonMarshalTester<T> { 062 063 private final ObjectMapper objectMapper; 064 065 private Class<?> view; 066 067 /** 068 * Create a new {@link JacksonTester} instance. 069 * @param objectMapper the Jackson object mapper 070 */ 071 protected JacksonTester(ObjectMapper objectMapper) { 072 Assert.notNull(objectMapper, "ObjectMapper must not be null"); 073 this.objectMapper = objectMapper; 074 } 075 076 /** 077 * Create a new {@link JacksonTester} instance. 078 * @param resourceLoadClass the source class used to load resources 079 * @param type the type under test 080 * @param objectMapper the Jackson object mapper 081 */ 082 public JacksonTester(Class<?> resourceLoadClass, ResolvableType type, 083 ObjectMapper objectMapper) { 084 this(resourceLoadClass, type, objectMapper, null); 085 } 086 087 public JacksonTester(Class<?> resourceLoadClass, ResolvableType type, 088 ObjectMapper objectMapper, Class<?> view) { 089 super(resourceLoadClass, type); 090 Assert.notNull(objectMapper, "ObjectMapper must not be null"); 091 this.objectMapper = objectMapper; 092 this.view = view; 093 } 094 095 @Override 096 protected T readObject(InputStream inputStream, ResolvableType type) 097 throws IOException { 098 return getObjectReader(type).readValue(inputStream); 099 } 100 101 @Override 102 protected T readObject(Reader reader, ResolvableType type) throws IOException { 103 return getObjectReader(type).readValue(reader); 104 } 105 106 private ObjectReader getObjectReader(ResolvableType type) { 107 ObjectReader objectReader = this.objectMapper.readerFor(getType(type)); 108 if (this.view != null) { 109 return objectReader.withView(this.view); 110 } 111 return objectReader; 112 } 113 114 @Override 115 protected String writeObject(T value, ResolvableType type) throws IOException { 116 return getObjectWriter(type).writeValueAsString(value); 117 } 118 119 private ObjectWriter getObjectWriter(ResolvableType type) { 120 ObjectWriter objectWriter = this.objectMapper.writerFor(getType(type)); 121 if (this.view != null) { 122 return objectWriter.withView(this.view); 123 } 124 return objectWriter; 125 } 126 127 private JavaType getType(ResolvableType type) { 128 return this.objectMapper.constructType(type.getType()); 129 } 130 131 /** 132 * Utility method to initialize {@link JacksonTester} fields. See {@link JacksonTester 133 * class-level documentation} for example usage. 134 * @param testInstance the test instance 135 * @param objectMapper the object mapper 136 * @see #initFields(Object, ObjectMapper) 137 */ 138 public static void initFields(Object testInstance, ObjectMapper objectMapper) { 139 new JacksonFieldInitializer().initFields(testInstance, objectMapper); 140 } 141 142 /** 143 * Utility method to initialize {@link JacksonTester} fields. See {@link JacksonTester 144 * class-level documentation} for example usage. 145 * @param testInstance the test instance 146 * @param objectMapperFactory a factory to create the object mapper 147 * @see #initFields(Object, ObjectMapper) 148 */ 149 public static void initFields(Object testInstance, 150 ObjectFactory<ObjectMapper> objectMapperFactory) { 151 new JacksonFieldInitializer().initFields(testInstance, objectMapperFactory); 152 } 153 154 /** 155 * Returns a new instance of {@link JacksonTester} with the view that should be used 156 * for json serialization/deserialization. 157 * @param view the view class 158 * @return the new instance 159 */ 160 public JacksonTester<T> forView(Class<?> view) { 161 return new JacksonTester<>(this.getResourceLoadClass(), this.getType(), 162 this.objectMapper, view); 163 } 164 165 /** 166 * {@link FieldInitializer} for Jackson. 167 */ 168 private static class JacksonFieldInitializer extends FieldInitializer<ObjectMapper> { 169 170 protected JacksonFieldInitializer() { 171 super(JacksonTester.class); 172 } 173 174 @Override 175 protected AbstractJsonMarshalTester<Object> createTester( 176 Class<?> resourceLoadClass, ResolvableType type, 177 ObjectMapper marshaller) { 178 return new JacksonTester<>(resourceLoadClass, type, marshaller); 179 } 180 181 } 182 183}