You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin SpringBoot测试中@MockBean Mock对象随机返回null问题

Kotlin结合Mockito开展SpringBoot测试时Mock随机失效问题

问题根因

  • Mock桩配置与生命周期不匹配:@MockBean注入的Mock对象,Spring Boot Test默认会在每个测试方法执行完成后自动清空所有桩配置。现有代码将Mock规则写在@BeforeAll标记的方法中,该方法仅在测试类初始化时执行一次,桩规则仅对第一个测试方法生效。第一个测试执行完成后Mock规则被清空,后续测试请求、测试收尾阶段未断开的SocketIO客户端发出的心跳/重连请求,调用authApi.authorize()时无匹配桩规则,Mockito会返回对象类型默认值null。
  • Kotlin空安全与Mockito默认行为兼容漏洞:业务代码中authApi.authorize()声明返回非空Authorization类型,但Mockito基于Java字节码生成代理类,无法感知Kotlin的非空约束,无匹配桩时直接返回null。现有代码在runCatching块中未将authorize()返回值赋值给显式声明的非空变量,Kotlin未在调用点插入运行时空检查,导致返回的null未触发异常进入onFailure分支,反而被判定为执行成功传入onSuccess回调,最终在AuthorizationContext.set(it)行触发空指针。
  • 异步请求时序不确定:SocketIO连接、心跳、事件交互均为异步逻辑,@BeforeAll中启动客户端连接后,请求可能在测试生命周期任意节点发出,包括测试方法间隙Mock被重置的窗口、测试上下文销毁阶段,进一步放大了Mock失效概率,表现为随机报错。

修复方案

  • 调整Mock桩配置时机:将Mock规则配置逻辑从@BeforeAll移到@BeforeEach方法中,每个测试方法执行前重新打桩,保证测试运行时桩规则始终有效;同时将参数匹配器从anyOrNull()替换为anyString(),匹配业务逻辑中token不可能为null的前置判断,减少匹配误判。
    对应配置代码示例:
@BeforeEach
fun setupMock() {
    Mockito.`when`(mockedApiComponent.authorize(anyString())).thenReturn(auth)
}
  • 补全Kotlin运行时空检查:在runCatching块中显式将授权结果赋值给非空类型变量,触发Kotlin内置的运行时空校验,一旦Mock返回null直接抛出异常进入onFailure分支,避免空值流入后续逻辑。
    修正后的过滤器代码片段:
runCatching {
    // 显式声明非空类型,触发Kotlin运行时空检查
    val authResult: Authorization = authApi.authorize(token.replace("Bearer ", ""))
    authResult
}.onSuccess {
    AuthorizationContext.set(it)
    filterChain.doFilter(request, response)
}.onFailure {
    it.printStackTrace()
    response.sendError(401)
}
  • 对齐异步组件生命周期:新增@AfterAll方法,测试执行完成后主动断开SocketIO客户端、停止SocketIO服务,避免测试结束后残留的异步请求调用已被重置的Mock对象。
    对应销毁代码示例:
@AfterAll
fun cleanup() {
    clientSocket.close()
    socketIOServer.stop()
}
  • 可选优化:引入mockito-kotlin库适配Kotlin空安全特性,或开启Mockito严格桩模式,测试过程中一旦出现未配置桩的方法调用直接抛出错误,提前暴露问题而非静默返回null。

内容的提问来源于stack exchange,提问作者Rafael Lima

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 18:51:05