Kotlin Android单元测试MockK指南

目录

  1. MockK 简介
  2. 环境配置
  3. 基础用法
  4. 高级用法
  5. Android 特有场景
  6. 最佳实践

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. 最佳实践

  1. 避免过度 Mock:仅 Mock 外部依赖(如网络、数据库),不要 Mock 被测类内部行为。
  2. 使用清晰命名 :如 userRepositoryMock 替代 mockk<Repository>()。
  3. 及时清理 :在 @After 中调用 unmockkAll() 避免测试间污染。
  4. 组合使用工具 :结合 Truth、Turbine 等库提升断言可读性。
  5. 优先使用真实对象:当依赖简单且无副作用时,直接使用真实对象而非 Mock。

实际开发中,建议结合具体场景选择最合适的 Mock 策略,确保测试既简洁又可靠。

相关推荐
小师兄吃牛肉1 小时前
Android Studio 2025 专业介绍:新特性、性能与开发体验全面解析
android·ide·android studio
美摄科技1 小时前
安卓离线类美颜特效SDK
android
fengci.3 小时前
BUUCTF(困难部分)
android·开发语言·前端·学习·php
传奇开心果4 小时前
【Compose Multiplatform跨端声明式UI实用技术】第9课:跨平台 ViewModel 与状态架构
android·kotlin
用户69371750013845 小时前
当你向领导申请 AI 编程工具预算时,如果领导追问“能提效多少,有量化数据吗”,你该怎么回答?
android·后端·ios
hunterandroid6 小时前
WorkManager 后台任务调度实战:从一次性任务到周期执行
android
事圆则缓6 小时前
Android 屏幕适配的前世今生
android
andyweike6 小时前
kotlin dsl library工程怎么配置
android·开发语言·kotlin
啵啵啵鱼6 小时前
九、JDBC 操作 MySQL 数据库
android·数据库·mysql
用户69371750013846 小时前
当我说 Claude 搭配 GLM-5.3-Flash 太好用了,同事却提醒我:你可能正在失去对代码的掌控
android·后端·ios