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 策略,确保测试既简洁又可靠。

相关推荐
美狐美颜SDK开放平台34 分钟前
直播APP开发核心功能详解:美颜SDK、音视频、礼物等功能成本怎么计算?
android·人工智能·计算机视觉·音视频·直播美颜sdk
安河桥。1 小时前
Android视频流处理模块硬件加速器数据流转技术说明
android·图像处理·isp
hunterandroid1 小时前
[Android 从零到一] APK 体积优化实战:从 50MB 到 15MB 的瘦身之旅
android·前端
迪飞特科技1 小时前
《MyBatis 批量插入性能优化的 5 个关键参数配置》
android·性能优化·mybatis
mmsx1 小时前
osmdroid 手势交互与罗盘方向:缩放联动+传感器融合+定位跟随
android·源码·地图·osmdroid
帅次2 小时前
Google Play 与 Android 17 内存治理:市场格局与趋势分析
android·google play·memory limiter
海兰2 小时前
【 Python 量化交易】第10章:八大经典策略
android·python·kotlin
Rytter2 小时前
Android诈骗裸聊软件逆向分析
android
JMchen1233 小时前
【Android 性能优化实战 60 讲】04 Memory Profiler 高阶玩法:深剖堆内存原理,实战定位 Kotlin 闭包隐式泄漏
android·性能优化·kotlin·实战·源码分析·内存泄漏·memory profiler
Android-Flutter3 小时前
Java线程池 - 内部线程管理机制详解
android