我试图将单元测试写入自定义反序列化器,该自解序列器使用带有@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(…)的反序列化器行为,但是这种方法在非Deserializer单元测试的测试中对我不起作用…
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); } }
一个非常有趣的问题,使我想知道自动插入杰克逊解串器在弹簧应用程序中如何工作。使用的jackson工具似乎是HandlerInstantiatorinterface,它是由spring配置到SpringHandlerInstantiator实现的,该实现只是在应用程序上下文中查找类。
HandlerInstantiator
SpringHandlerInstantiator
因此,从理论上讲,您可以ObjectMapper使用自己的(模拟的)在单元测试中设置一个,HandlerInstantiator从中返回一个准备好的实例deserializerInstance()。返回null其他方法似乎很好,或者当class参数不匹配时,这将导致jackson自行创建实例。
ObjectMapper
deserializerInstance()
null
但是,我认为这不是对反序列化逻辑进行单元测试的好方法,因为ObjectMapper设置必然与实际应用程序执行过程中使用的设置不同。使用JsonTestAnton答案中建议将是一种更好的方法,因为您将获得与运行时相同的json配置。
JsonTest