【发布时间】:2020-05-20 12:02:56
【问题描述】:
我正在尝试将单元测试写入自定义反序列化程序,该反序列化程序使用带有@Autowired 参数的构造函数和我的用@JsonDeserialize 标记的实体进行实例化。它在我的集成测试中运行良好,其中 MockMvc 带来了 spring 服务器端。
但是,在调用 objectMapper.readValue(...) 的测试中,使用没有参数的默认构造函数的反序列化器的新实例被实例化。虽然
@Bean
public MyDeserializer deserializer(ExternalObject externalObject)
实例化解串器的有线版本,真正的调用仍然传递给空的构造函数并且上下文没有被填充。
我尝试手动实例化反序列化器实例并将其注册到 ObjectMapper 中,但它仅在我从实体类中删除 @JsonDeserialize 时才有效(即使我在 @Configuration 类中执行相同操作,它也会破坏我的集成测试。) - 看起来与此相关:https://github.com/FasterXML/jackson-databind/issues/1300
我仍然可以直接调用 deserializer.deserialize(...) 来测试反序列化器的行为,但是这种方法在不是反序列化器单元测试的测试中对我不起作用...
UPD:下面的工作代码
import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
import com.github.tomakehurst.wiremock.common.Json;
import org.junit.Assert;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
import java.io.IOException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@JsonTest
@RunWith(SpringRunner.class)
public class JacksonInjectExample {
private static final String JSON = "{\"field1\":\"value1\", \"field2\":123}";
public static class ExternalObject {
@Override
public String toString() {
return "MyExternalObject";
}
}
@JsonDeserialize(using = MyDeserializer.class)
public static class MyEntity {
public String field1;
public String field2;
public String name;
public MyEntity(ExternalObject eo) {
name = eo.toString();
}
@Override
public String toString() {
return name;
}
}
@Component
public static class MyDeserializer extends JsonDeserializer<MyEntity> {
@Autowired
private ExternalObject external;
public MyDeserializer() {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
public MyDeserializer(@JacksonInject final ExternalObject external) {
this.external = external;
}
@Override
public MyEntity deserialize(JsonParser p, DeserializationContext ctxt) throws IOException,
JsonProcessingException {
return new MyEntity(external);
}
}
@Configuration
public static class TestConfiguration {
@Bean
public ExternalObject externalObject() {
return new ExternalObject();
}
@Bean
public MyDeserializer deserializer(ExternalObject externalObject) {
return new MyDeserializer(externalObject);
}
}
@Test
public void main() throws IOException {
HandlerInstantiator hi = mock(HandlerInstantiator.class);
MyDeserializer deserializer = new MyDeserializer();
deserializer.external = new ExternalObject();
doReturn(deserializer).when(hi).deserializerInstance(any(), any(), eq(MyDeserializer.class));
final ObjectMapper mapper = Json.getObjectMapper();
mapper.setHandlerInstantiator(hi);
final MyEntity entity = mapper.readValue(JSON, MyEntity.class);
Assert.assertEquals("MyExternalObject", entity.name);
}
}
【问题讨论】:
标签: java spring spring-boot jackson json-deserialization