如何Mock Kotlin的map函数?测试遇空指针及返回值错误
解决Mock Kotlin扩展函数
map时的测试异常问题 这个问题的核心在于Kotlin的map是扩展函数,而Mockito默认只能Mock对象的成员方法,直接Mock扩展函数会触发一系列底层依赖问题,比如你遇到的NullPointerException和返回值类型错误。我们一步步来拆解原因并给出解决方案:
为什么会出现这些错误?
- NPE的根源:Kotlin的
Iterable.map扩展函数内部会调用对象的iterator()方法。当你MockGenericRawResults<User>时,Mockito默认不会为iterator()生成有效实现,所以执行map时,for (item in this)会尝试遍历一个null迭代器,直接抛出NPE。 - 返回值类型错误的根源:你尝试用
onGenericMockmapTo时,Mockito错误地把这个扩展函数当成了GenericRawResults的成员方法,当mapTo内部调用iterator()时,Mockito试图让iterator()返回你设置的ArrayList,但iterator()原本应该返回Iterator类型,因此抛出类型不匹配的错误。
解决方案:Mock底层成员方法而非扩展函数
既然扩展函数依赖对象的成员方法(比如iterator()),我们可以直接Mock这些核心成员方法,让标准库的扩展函数能正常执行,而不是去Mock扩展函数本身。
具体实现代码
private fun stubGetUsers(users: List<User>) { val genericRawResult = mock<GenericRawResults<User>>() // 关键:Mock iterator() 方法,让它返回一个包含测试数据的迭代器 whenever(genericRawResult.iterator()).thenReturn(users.iterator()) // 不需要Mock map方法,真实的map扩展函数会用上面的迭代器生成正确结果 usersDao.stub { on { it.queryRaw("", RawRowMapper { _, _ -> User() }) }.doReturn(genericRawResult) } }
替代方案:使用Mockito处理扩展函数(进阶)
如果你确实需要直接Mock扩展函数(比如扩展函数有自定义逻辑),可以使用Mockito-inline结合静态Mocking来处理Kotlin的顶层扩展函数(因为map本质是kotlin.collections.CollectionsKt中的静态方法):
- 确保你的项目依赖了
mockito-inline(替代默认的mockito-core)。 - 用
mockStatic来MockCollectionsKt类,然后指定map方法的返回值:
private fun stubGetUsers(users: List<User>) { val genericRawResult = mock<GenericRawResults<User>>() mockStatic(kotlin.collections.CollectionsKt::class.java) { whenever( it.invoke<Iterable<User>, User>( genericRawResult, any<(User) -> User>() ) ).thenReturn(users) } usersDao.stub { on { it.queryRaw("", RawRowMapper { _, _ -> User() }) }.doReturn(genericRawResult) } }
不过这种方式相对复杂,而且静态Mock可能会影响其他测试用例,所以优先推荐第一种方案——Mock底层成员方法。
总结
- Kotlin扩展函数不是对象的成员方法,Mockito无法直接Mock,优先Mock扩展函数依赖的成员方法(如
iterator())。 - 避免尝试Mock标准库的扩展函数,而是让它们基于Mock后的成员方法正常执行。
内容的提问来源于stack exchange,提问作者Zbarcea Christian
相关产品推荐
相关产品推荐

