Spring Boot中带非空构造函数的自定义反序列化器单元测试问题
我有一个Spring Boot MVC应用,为MyObject类使用了自定义反序列化器。该自定义反序列化器类包含一个带参数的构造函数:
@JsonDeserialize(using = MyObjectDeserializer.class) class MyObject { . . . } @Component class MyObjectDeserializer extends JsonDeserializer<MyObject> { private AnotherObject anotherObject; @Autowired public MyObjectDeserializer(AnotherObject anotherObject) { this.anotherObject = anotherObject; } ... }
运行应用时一切正常,这是Spring自动装配机制在起作用:相关组件会创建ObjectMapper Bean,以及AnotherObject和MyObjectDeserializer的Bean。但在单元测试中该机制失效:
@RunWith(SpringRunner.class) public void MyObjectDeserTest { @Autowired private ObjectMapper objectMapper; }
测试中objectMapper为null。请问如何在单元测试中正确初始化ObjectMapper,使反序列化器能正确加载依赖?或者是否有更合理的方式为反序列化器注入对象?
方案一:修正单元测试配置
你的测试类缺少Spring Boot的测试启动注解,导致Spring上下文未正确初始化,所以ObjectMapper无法被注入。修改测试类如下:
@RunWith(SpringRunner.class) @SpringBootTest // 添加上该注解,启动Spring Boot上下文 public class MyObjectDeserTest { // 注意这里是class而非void @Autowired private ObjectMapper objectMapper; // 测试方法示例 @Test public void testDeserialization() throws JsonProcessingException { String json = "{...}"; MyObject result = objectMapper.readValue(json, MyObject.class); // 编写断言逻辑 } }
如果只想加载必要Bean而非完整应用上下文,可以使用@WebMvcTest或自定义配置类配合@ContextConfiguration:
@RunWith(SpringRunner.class) @ContextConfiguration(classes = {MyObjectDeserializer.class, AnotherObject.class, JacksonAutoConfiguration.class}) public class MyObjectDeserTest { @Autowired private ObjectMapper objectMapper; // 测试方法 }
方案二:改进反序列化器的注入方式
方式1:自定义Jackson配置注册反序列化器
移除MyObject类上的@JsonDeserialize(using = MyObjectDeserializer.class)注解,通过配置类手动注册反序列化器:
@Configuration public class JacksonConfig { @Autowired private MyObjectDeserializer myObjectDeserializer; @Bean public ObjectMapper objectMapper() { ObjectMapper objectMapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addDeserializer(MyObject.class, myObjectDeserializer); objectMapper.registerModule(module); return objectMapper; } }
这种方式下,单元测试只要加载JacksonConfig及相关依赖Bean,就能获取配置正确的ObjectMapper。
方式2:使用@JsonComponent自动注册
用@JsonComponent标记反序列化器,Spring Boot会自动识别并将其注册到ObjectMapper中,无需手动配置:
@JsonComponent // 替换原有的@Component和@JsonDeserialize的using属性 class MyObjectDeserializer extends JsonDeserializer<MyObject> { private AnotherObject anotherObject; @Autowired public MyObjectDeserializer(AnotherObject anotherObject) { this.anotherObject = anotherObject; } ... }
此时MyObject类上的@JsonDeserialize(using = ...)可直接移除,Spring会自动关联反序列化器,单元测试中启动Spring上下文即可正常注入ObjectMapper。
内容的提问来源于stack exchange,提问作者Haster

