如何配置或Mock Vert.x JsonObject.mapFrom解决LocalDateTime序列化问题
问题场景
测试过程中抛出序列化异常:java.lang.IllegalArgumentException: Java 8 date/time type java.time.LocalDateTime not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling
已知约束:
- 已引入
jackson-datatype-jsr310依赖,自定义ObjectMapper中已注册JavaTimeModule,自定义序列化逻辑运行正常 - 业务代码通过
io.vertx.core.json.JsonObject.mapFrom()完成Java对象到JSON的转换,该方法未使用自定义配置的ObjectMapper,持续抛出上述LocalDateTime序列化错误 - 测试阶段无权修改主业务代码:既不能替换原有
mapFrom调用逻辑,也不能在实体类添加序列化注解,常规方式无法直接MockJsonObject.mapFrom静态方法
涉及业务代码片段:
// Class1类中的被测方法 public void getSomething(Request request){ JsonObject jsonObject = JsonObject.mapFrom(helper); // 后续jsonObject相关业务逻辑 }
// 包含LocalDateTime字段的Helper类 public class Helper { LocalDateTime createdAt = LocalDateTime.now(); // 该字段触发序列化报错 }
问题答复
1. 是否可通过ObjectMapper配置修改JsonObject.mapFrom方法的默认序列化行为?
可以,无需修改任何业务代码。JsonObject.mapFrom()底层默认使用Vert.x框架自身持有的全局静态ObjectMapper实例,不会感知业务代码中自定义构造的ObjectMapper配置。你只需要在测试启动阶段(比如测试类的@BeforeAll初始化节点),直接修改Vert.x全局持有的这个ObjectMapper实例,注册对应序列化模块即可。
实现代码:
import io.vertx.core.json.Json; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.databind.SerializationFeature; import org.junit.jupiter.api.BeforeAll; @BeforeAll public static void initVertxJsonConfig() { // 拿到Vert.x内部默认使用的全局ObjectMapper ObjectMapper defaultMapper = Json.mapper; // 注册JavaTimeModule支持Java8时间类型序列化 defaultMapper.registerModule(new JavaTimeModule()); // 按需配置:关闭时间戳格式,按ISO字符串格式输出时间 defaultMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); }
说明:这个全局Mapper是Vert.x官方开放给用户做配置扩展的入口,配置完成后所有调用JsonObject.mapFrom/Json.encode等Vert.x自带JSON序列化方法的逻辑都会使用该配置,不存在侵入业务代码的问题,只需要保证配置在mapFrom方法被调用前执行即可。
2. 是否有可行方案可Mock该静态方法,无需修改业务代码即可完成测试?
可以,使用支持静态方法Mock的测试工具即可,无需改动业务代码。
目前Mockito 3.4.0及以上版本原生支持静态方法Mock,不需要引入PowerMock等额外组件,实现方式如下:
import org.mockito.MockedStatic; import org.mockito.Mockito; import static org.mockito.ArgumentMatchers.any; import org.junit.jupiter.api.Test; @Test void testGetSomethingLogic() { // 提前构造测试数据和预期返回的JSON对象 Request testRequest = new Request(); JsonObject mockJson = new JsonObject() .put("createdAt", "2024-05-20T14:30:00"); // 开启JsonObject类的静态Mock,try-with-resources块执行完后会自动关闭Mock,避免污染其他测试用例 try (MockedStatic<JsonObject> jsonObjectMock = Mockito.mockStatic(JsonObject.class)) { // 桩逻辑:当传入Helper类型参数调用mapFrom时,返回提前构造好的mockJson jsonObjectMock.when(() -> JsonObject.mapFrom(any(Helper.class))) .thenReturn(mockJson); // 执行被测方法 class1.getSomething(testRequest); // 后续执行你的断言逻辑,验证业务行为是否符合预期 // ... } }
注意:静态Mock的作用域必须控制在单个测试用例范围内,不要全局开启,避免影响其他测试用例的正常执行。如果使用低于3.4.0版本的Mockito,也可以通过PowerMock实现同等的静态方法Mock效果,配置逻辑类似。
内容的提问来源于stack exchange,提问作者ss13199

