目录
- MockK 简介
- 环境配置
- 基础用法
- 高级用法
- Android 特有场景
- 最佳实践
1. MockK 简介
MockK 是一个专为 Kotlin 设计的 Mocking 框架,支持协程、扩展函数、对象声明(object
)等 Kotlin 特性。相比 Mockito,它提供更自然的 Kotlin API,解决了 final
类无法 Mock 的问题。
核心优势:
- 原生支持 Kotlin 特性(如协程、
object
单例)。 - 简洁的 DSL 语法。
- 支持静态方法、构造函数 Mock。
2. 环境配置
在 build.gradle
中添加依赖:
kotlin
// 模块级 build.gradle
dependencies {
testImplementation "io.mockk:mockk:1.13.8" // 基础库
testImplementation "io.mockk:mockk-agent-jvm:1.13.8" // 解决某些 JDK 版本兼容性问题
testImplementation "org.junit.jupiter:junit-jupiter:5.8.1" // JUnit 5(可选)
}
注意 :若使用 JUnit 4,需添加
testImplementation "io.mockk:mockk-android:1.13.8"
。
3. 基础用法
3.1 创建 Mock 对象
kotlin
val service = mockk<MyService>() // 创建 Mock 对象
3.2 设置行为 (Stubbing)
kotlin
// 模拟方法返回值
every { service.fetchData(any()) } returns "Mocked Data"
// 模拟抛出异常
every { service.fail() } throws RuntimeException("Error")
3.3 验证调用
kotlin
service.fetchData(123)
verify { service.fetchData(123) } // 验证方法是否被调用
// 验证调用次数
verify(exactly = 1) { service.fetchData(any()) }
3.4 参数匹配器
kotlin
// 匹配任何参数
every { service.fetchData(any()) } returns "Data"
// 捕获参数
val slot = slot<Int>()
every { service.saveData(capture(slot)) } just Runs
service.saveData(123)
assert(slot.captured == 123)
4. 高级用法
4.1 模拟静态方法与对象声明
kotlin
// Mock object 单例
object Singleton {
fun doWork() = "Real"
}
mockkObject(Singleton)
every { Singleton.doWork() } returns "Mocked"
// Mock 静态方法
mockkStatic(MyUtils::class)
every { MyUtils.format(any()) } returns "Formatted"
清理资源:
kotlin
@After
fun tearDown() {
unmockkAll() // 或 unmockkObject(Singleton)
}
4.2 模拟构造函数
kotlin
class MyHelper(val config: String)
mockkConstructor(MyHelper::class)
every { anyConstructed<MyHelper>().config } returns "Mocked Config"
val helper = MyHelper("Real")
assert(helper.config == "Mocked Config")
4.3 协程支持
使用 coEvery
和 coVerify
处理挂起函数:
kotlin
class Repository {
suspend fun loadData() = "Real Data"
}
val repo = mockk<Repository>()
coEvery { repo.loadData() } returns "Mocked Data"
runBlocking {
val data = repo.loadData()
assert(data == "Mocked Data")
coVerify { repo.loadData() }
}
4.4 Spy:部分模拟真实对象
kotlin
val spy = spyk<RealClass>() // 默认调用真实方法
every { spy.mockedMethod() } returns "Mocked"
5. Android 特有场景
5.1 测试 ViewModel
kotlin
class MyViewModel(private val repo: Repository) : ViewModel() {
private val _data = MutableLiveData<String>()
val data: LiveData<String> = _data
fun load() {
viewModelScope.launch {
_data.value = repo.fetchData()
}
}
}
// 测试代码
@Test
fun testViewModel() = runTest { // 使用 TestCoroutineDispatcher
val repo = mockk<Repository>()
coEvery { repo.fetchData() } returns "Test Data"
val viewModel = MyViewModel(repo)
viewModel.load()
// 处理 LiveData
val observer = mockk<Observer<String>>()
viewModel.data.observeForever(observer)
verify(timeout = 1000) { observer.onChanged("Test Data") }
}
5.2 处理 Context
kotlin
val context = mockk<Context>()
val res = mockk<Resources>()
every { context.resources } returns res
every { res.getString(any()) } returns "Mocked String"
6. 最佳实践
- 避免过度 Mock:仅 Mock 外部依赖(如网络、数据库),不要 Mock 被测类内部行为。
- 使用清晰命名 :如
userRepositoryMock
替代mockk<Repository>()
。 - 及时清理 :在
@After
中调用unmockkAll()
避免测试间污染。 - 组合使用工具 :结合
Truth
、Turbine
等库提升断言可读性。 - 优先使用真实对象:当依赖简单且无副作用时,直接使用真实对象而非 Mock。
实际开发中,建议结合具体场景选择最合适的 Mock 策略,确保测试既简洁又可靠。