如何用Kotlin+MockK模拟带限定类型参数的Java静态方法?
用MockK模拟带限定泛型参数的Java静态方法并捕获参数
能否实现?
可以,MockK支持处理Java的限定泛型参数(T extends C & I),在Kotlin中这类约束会被解析为交叉类型C & I,我们可以基于这个特性实现参数捕获。
具体实现方案
1. 使用Slot<C & I>捕获参数
直接定义交叉类型的Slot,配合capture()完成参数捕获,代码示例:
import io.mockk.every import io.mockk.mockk import io.mockk.mockkStatic import io.mockk.slot import org.junit.Test class Test { @Test fun testDoSomething() { // 静态mock Java类A mockkStatic(A::class) // 定义交叉类型的Slot,匹配所有继承C且实现I的类型 val thingSlot = slot<C & I>() // 配置mock行为:捕获参数并返回mock对象 every { A.doSomething(capture(thingSlot)) } returns mockk() // 执行被测试代码,触发A.doSomething调用 val testInstance = object : C(), I {} // 任意符合约束的实例 A.doSomething(testInstance) // 验证捕获的参数 assert(thingSlot.captured == testInstance) assert(thingSlot.captured is C) assert(thingSlot.captured is I) } }
2. 解决类型推断问题
如果遇到Kotlin类型推断报错,可以显式指定泛型参数:
every { A.doSomething<C & I>(capture(thingSlot)) } returns mockk()
替代方案(无Slot的手动捕获)
如果交叉类型的Slot使用有问题,也可以通过answers块手动捕获参数:
@Test fun testDoSomethingAlternative() { mockkStatic(A::class) var capturedThing: (C & I)? = null every { A.doSomething(any()) } answers { // 从调用参数中获取第一个参数,强制转换为交叉类型 capturedThing = firstArg() as C & I mockk() } // 触发调用 val testInstance = object : C(), I {} A.doSomething(testInstance) // 验证 assert(capturedThing == testInstance) }
注意事项
- 静态mock的作用域:建议使用
mockkStatic(A::class) { ... }的lambda形式,自动在块结束后解除mock,避免污染其他测试。 - 类型安全:交叉类型的转换在运行时是安全的,因为只有符合
C & I约束的实例才会被传入doSomething方法。
内容的提问来源于stack exchange,提问作者Saar
相关产品推荐
相关产品推荐

