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
相关产品推荐
相关产品推荐

