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 协程支持

使用 coEverycoVerify 处理挂起函数:

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. 组合使用工具 :结合 TruthTurbine 等库提升断言可读性。
  5. 优先使用真实对象:当依赖简单且无副作用时,直接使用真实对象而非 Mock。

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

相关推荐
hjlgs8 小时前
framework修改快速验证
android
游戏开发爱好者88 小时前
iOS 开发者的安全加固工具,从源码到成品 IPA 的多层防护体系实践
android·安全·ios·小程序·uni-app·cocoa·iphone
安卓理事人9 小时前
安卓内存泄露排查LeakCanary
android
秃了也弱了。10 小时前
MySQL空间函数详解,MySQL记录经纬度并进行计算
android·数据库·mysql
.豆鲨包11 小时前
【Android】Binder机制浅析
android·binder
Nerve12 小时前
GooglePay: API 文档
android·google
Nerve13 小时前
GooglePay: 订阅商品购买流程
android·google
summerkissyou198713 小时前
Audio-触摸音-播放流程
android
Nerve13 小时前
GooglePay: 消耗商品购买流程
android·google
LiteHeaven13 小时前
Android 8.1 Unable to create application data 问题分析
android