主流Mock框架对比:Mockito、MockK、PowerMock的特点与选型
做后台Mock回放,选哪个框架?这个问题我几乎每次接手新项目都要面对。说实话,没有银弹。Mockito、MockK、PowerMock 各有各的脾气。下面我结合自己踩过的坑,把三者的特点掰开聊聊。
Mockito:最成熟的“老大哥”
Mockito 在 Android 圈子里用得最广。我个人习惯用它来 mock 接口和普通类。不过它有个硬伤——不支持 mock 静态方法和 final 类。我曾经在一个遗留项目里碰到一堆工具类全是静态方法,Mockito 直接无能为力。
但大多数场景下,Mockito 够用了。它的语法很流畅,比如:
// 创建 mock
val mockService = Mockito.mock(ApiService::class.java)
`when`(mockService.getUser()).thenReturn(User("test"))
嗯,注意这里要用反引号括住 when,因为 Kotlin 的保留字。另外,Mockito 的版本更新到现在已经支持了 inline mock,但还是有限制。
MockK:Kotlin 生态的“亲儿子”
如果你用 Kotlin 写后台回放逻辑,我建议优先考虑 MockK。它原生支持 Kotlin 的协程、扩展函数、object 单例等特性。我自己在写 mock 回放项目时,遇到协程的 suspend 函数,MockK 一行注解就搞定了。
对比亮点:
- 支持协程:
coEvery { service.call() } coReturns ... - 支持顶层函数:
mockkStatic("com.example.Utils") - 支持 object:
mockkObject(MySingleton)
举个例子,mock 一个 ViewModel 的 suspend 函数:
val repo = mockk<UserRepository>()
coEvery { repo.getUser() } returns User("mock")
这比 Mockito 的 Answer 要简洁多了。不过 MockK 的学习曲线稍微陡一点,尤其是它的 slot、capture 机制,我第一次用的时候也懵了很久。
PowerMock:打破规则的“暴力工具”
说到 PowerMock,我的心情有点复杂。它几乎能 mock 一切——静态方法、私有方法、构造方法、甚至 final 类。但代价是它修改了 classloader,运行慢,而且容易和其他框架冲突。我曾经在一个 module 里同时用了 PowerMock 和 Robolectric,结果跑单元测试花了半个小时。
所以我的原则是:能用 Mockito 或 MockK 解决,不上 PowerMock。只有在遗留代码、第三方库无法改动时才考虑它。比如你要 mock System.currentTimeMillis() 返回固定值,PowerMock 可以这样:
@RunWith(PowerMockRunner::class)
@PrepareForTest(System::class)
class TimeTest {
@Test
fun test() {
PowerMockito.mockStatic(System::class.java)
`when`(System.currentTimeMillis()).thenReturn(1000L)
}
}
但注意,PowerMock 依赖 JUnit4 的 Runner,如果你项目已经迁移到 JUnit5 或者使用 AndroidX Test,集成起来可能头疼。
核心对比一览
| 维度 | Mockito | MockK | PowerMock |
|---|---|---|---|
| 支持静态方法 | ❌(需 inline mock) | ✅(mockkStatic) | ✅ |
| 支持 final 类 | ❌ | ✅ | ✅ |
| 协程支持 | ❌(需手写 Answer) | ✅ 原生 | ❌ |
| 性能 | 快 | 中等 | 慢 |
| 学习成本 | 低 | 中 | 高 |
| 兼容性(AndroidX) | 好 | 好 | 差 |
选型建议:我的实战经验
你想想看,你的项目是什么情况?
- 纯 Java 项目 + 普通接口 mock: 无脑用 Mockito,社区资源最多。
- Kotlin 项目 + 协程 / 扩展函数: 我推荐 MockK,写起来最顺手。
- 必须 mock 静态方法、私有方法: 试试 Mockito 3.4+ 的 inline mock(
mockito-inline),不行再上 PowerMock。 - 单元测试速度和稳定性优先: 远离 PowerMock,它容易让测试变得脆弱。
记得我在一个大型后台回放项目中,一开始用了 PowerMock 来 mock 网络库的静态工厂,结果一次升级导致所有测试挂掉。后来换成了 Mockito + 接口重构,世界清净了。所以,框架选型其实也是架构设计的体现——尽量让你的代码对 mock 友好,而不是让 mock 框架去适配你的代码。
避坑指南: 如果你混合使用多个 mock 框架,注意类加载顺序。我曾经因为同时引入 Mockito 和 PowerMock 的 runner,导致 java.lang.VerifyError。解决方案是把 PowerMock 相关的测试单独放在一个 sourceSet 里。
选型决策流程
下面这张图帮你快速理清思路:
小技巧: 如果你的项目同时需要多种 mock 能力,可以考虑用 Mockito + MockK 组合,各自负责擅长的领域。但千万别把 PowerMock 放进来混用,否则你会和我一样后悔的。