添加ZonedDateTime模块后Mockito返回值为Null的问题求助
问题背景
在Spring Boot项目编写接口测试时,原本使用Date类型时测试正常。改用ZonedDateTime类型后,添加了com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.5依赖,并在测试代码中注册了JavaTimeModule,但此时Mockito模拟的bankStatementService.getBankStatementList方法返回值为Null,导致createSuccessResponse方法因参数非空却传入Null而抛出空指针异常。
接口代码
@PostMapping("/bankStatement") fun getBankStatementList(@Valid @RequestBody request: GenericRequest<BankStatementRequestDTO>): ResponseEntity<ResponseDTO> { return createSuccessResponse(bankStatementService.getBankStatementList(request.data)) }
测试代码
fun getBankStatementListSuccess() { val DEFAULT_DATE:ZonedDateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(0L), ZoneOffset.UTC); val request = GenericRequest( BankStatementRequestDTO( 10, 1, "", emptyList(), DEFAULT_DATE, DEFAULT_DATE, "03b3238c-66bb-4a4a-b205-4a9e8231b420" ) ) val response = BankStatementListResponseDTO( listOf( BankStatementResponseDTO( "XXX123", "CREDIT", "upi-123213", "03b3238c-66bb-4a4a-b205-4a9e8231b420", "UPI", BigDecimal(1234), "remarks", DEFAULT_DATE, DEFAULT_DATE, "CLAIMED", "COIN12345678900" ) ), 1 ) Mockito.`when`(bankStatementService.getBankStatementList(request.data)).thenReturn(response) val objectMapper = jacksonObjectMapper() objectMapper.registerModule(JavaTimeModule()) mvc.perform( MockMvcRequestBuilders .post("/api/v1/bankStatement") .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request)) ).andExpect(MockMvcResultMatchers.status().isOk) .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON)) }
错误信息
Request processing failed; nested exception is java.lang.NullPointerException: Parameter specified as non-null is null: method com.coinomerelaunch.brokeradminbackend.shared.dto.ResponseAdapterKt.createSuccessResponse, parameter dto org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException: Parameter specified as non-null is null: method com.coinomerelaunch.brokeradminbackend.shared.dto.ResponseAdapterKt.createSuccessResponse, parameter dto at app//org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
问题原因
Mockito的方法匹配依赖对象的相等性判断。测试中构造的BankStatementRequestDTO实例,和接口通过Jackson反序列化得到的实例不是同一个对象;且ZonedDateTime的序列化/反序列化细节(比如时区格式、序列化字符串的一致性),加上如果BankStatementRequestDTO未正确重写equals和hashCode方法,会导致两个对象的相等性判断失败,Mockito无法匹配到预设的方法调用,最终返回默认的Null值。
解决方案
1. 使用Mockito参数匹配器
放弃直接用具体对象匹配,改用匹配器确保方法调用被正确捕获:
// 匹配任意BankStatementRequestDTO类型参数 Mockito.`when`(bankStatementService.getBankStatementList(any(BankStatementRequestDTO::class.java))).thenReturn(response) // 或自定义字段校验逻辑 Mockito.`when`(bankStatementService.getBankStatementList(argThat { it.pageSize == 10 && it.pageNumber == 1 && it.merchantId == "03b3238c-66bb-4a4a-b205-4a9e8231b420" })).thenReturn(response)
2. 确保DTO重写equals和hashCode
如果BankStatementRequestDTO不是用Kotlin的data class定义的,手动基于所有字段(包括ZonedDateTime字段)重写equals和hashCode方法;如果是data class,Kotlin会自动生成正确的相等性逻辑,能保证序列化前后的对象相等。
3. 统一ObjectMapper配置
测试中手动创建的ObjectMapper可能和Spring容器中的配置不一致,导致ZonedDateTime序列化/反序列化存在差异。建议注入Spring管理的ObjectMapper并确保其注册了JavaTimeModule:
@Autowired lateinit var objectMapper: ObjectMapper @BeforeEach fun setUp() { objectMapper.registerModule(JavaTimeModule()) }
内容的提问来源于stack exchange,提问作者Anurag Parothia

