主流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,集成起来可能头疼。

核心对比一览

维度MockitoMockKPowerMock
支持静态方法❌(需 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 成熟稳定 适合普通Java/Kotlin接口 不支持静态 & final 学习成本低 MockK Kotlin生态原生 协程 / 扩展函数支持 object单例直接mock 学习成本中等 PowerMock mock一切(静态/私有) 修改classloader 速度慢、兼容性差 仅作最后手段 选型建议 根据项目技术栈和测试需求选择 优先 Mockito/MockK,PowerMock 慎用 ⚠️ 常见陷阱 混合框架注意类加载冲突 PowerMock 影响测试运行速度

小技巧: 如果你的项目同时需要多种 mock 能力,可以考虑用 Mockito + MockK 组合,各自负责擅长的领域。但千万别把 PowerMock 放进来混用,否则你会和我一样后悔的。