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

Mockito中then方法内使用any()匹配器的问题咨询

Mockito then方法中使用any()系列匹配器的问题解析

核心原因

Mockito的any()、eq()这类匹配器有严格的使用场景限制:它们只能出现在Mockito存根(whenever)或验证(verify)方法的参数位置,用来匹配方法调用时传入的参数。

你遇到的问题本质:

  • 第一个存根里用anyLong()生成返回值属于误用,只是碰巧没触发异常(比如Payload类的id字段是可空类型),但这不符合Mockito设计规范。
  • 第二个存根里尝试用eq("any-string")构造返回对象时,eq()不在Mockito方法的参数位置,Mockito不会对它做特殊处理,eq()本身会返回null,而Kotlin非空类型不允许null,因此抛出空指针异常。
  • 第三个存根里的anyLong()是正确用法(在whenever(userRepository.findById(anyLong()))的参数位置),你误解了"无法使用匹配器"——这里的匹配器使用是合规的,问题出在返回值里误用匹配器的场景。

正确使用方式

1. 存根时用匹配器定义触发条件,返回值用具体测试数据

不需要在返回值里用匹配器,直接构造符合测试需求的对象即可:

// given.2 修正版
whenever(authApiClient.refreshToken(any())).then {
    println(">>> authApiClient.refreshToken")
    ResponseEntity.ok().body(
        IssueTokenResponse(
            payload = IssueTokenResponse.Payload(
                "test-access-token", // 具体测试字符串
                3600L, // 具体过期时间
                "test-refresh-token"
            )
        )
    )
}

2. 需要根据传入参数动态生成返回值时,用it.arguments获取实际参数

如果返回值依赖方法被调用时传入的参数,不要用匹配器,而是通过then块的InvocationOnMock对象(即it)获取实际参数:

// 示例:根据传入的refreshToken返回对应的用户ID
whenever(authApiClient.validateRefreshToken(anyString())).then {
    // 获取调用时传入的第一个参数(即refreshToken字符串)
    val inputRefreshToken = it.arguments[0] as String
    // 模拟根据token生成用户ID的逻辑
    val userId = if (inputRefreshToken.startsWith("valid-")) 1001L else 0L
    RefreshTokenValidateResponse(
        payload = RefreshTokenValidateResponse.Payload(userId)
    )
}

3. 验证时的匹配器使用(正确场景)

你在then块里的验证代码是正确的,匹配器用在verify的参数位置:

// then 这部分无需修改
verify(authApiClient, times(1)).validateRefreshToken(anyString())
verify(authApiClient, times(1)).refreshToken(any())

4. 绝对避免在非Mockito方法参数位置使用匹配器

永远不要用any()、eq()来构造普通对象的属性,比如:

// 错误写法:eq()出现在普通构造函数参数里
IssueTokenResponse.Payload(eq("any-string"), anyLong(), eq("any-string"))

// 正确写法:用具体值或动态获取的参数
IssueTokenResponse.Payload("test-value", 123L, "test-value")

额外优化点

  • Kotlin中Mockito匹配器可能因非空类型触发异常,建议改用io.mockk:mockk库,它对Kotlin空安全支持更友好,语法也更简洁。
  • 你的@BeforeEach里手动实例化userSignInProvider属于冗余操作,@InjectMocks会自动注入依赖并创建实例,去掉这行可避免重复初始化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 05:15:37