技术目标
MockK是一款功能强大、易于使用的Kotlin mocking框架。它具有简洁的语法和强大的功能,能够过帮助开发者轻松的进行单元测试、集成测试。MockK提供了一套丰富灵活的API,可以轻松地创建模拟对象并进行相关的操作,来验证方法调用和预期的返回值。另外,它还提供了Mockito、PowerMock等不具备的高级功能,例如mock静态类、final类等。本文将介绍MockK在KMP中的基本使用方法,并深入探讨一些额外的高级特性。
前期分析
由于Mockito、PowerMockito主要针对Java语言进行的设计,因此在处理kotlin语言上存在缺陷。MockK是从零开始专门为Kotlin构建,它能够针对Kotlin实现更强大和高级的功能。
Mockito存在的问题
- 类型:Mockito不支持对 final class、匿名内部类以及基本类型(如 int)的 mock。
- 方法:Mockito不支持对静态方法、 final 方法、私有方法、equals() 和 hashCode() 方法进行 mock。
- 关键字:Mockito使用时when要加上反引号才能使用(与kotlin关键字when冲突),这种写法非常不友好。
PowerMockito存在的问题
- 兼容性:在Android上编写单元测试,使用了某版本无法支持对静态方法与final类进行mock
MockK的优势
- 强大的mock能力:MockK支持final class、匿名内部类以及基本类型的mock,同时支持静态、final方法的mock。
- 简化测试代码:MockK提供了简洁而直观的 API,使得创建和管理模拟对象变得容易。它的语法清晰简洁,可以快速定义模拟对象的行为和预期结果,从而减少冗余的测试代码。
- 模拟复杂场景:MockK不仅可以模拟普通的对象行为,还可以处理更复杂的场景,如模拟 lambda 表达式、捕获函数调用参数等。这使得在测试中处理回调函数、异步操作或依赖其他组件的情况变得更加容易。
- 支持依赖注入框架:MockK可以与常见的依赖注入框架(如Koin、Dagger)集成,使得在单元测试中模拟依赖项变得更加便捷。通过模拟依赖项,我们可以更好地隔离被测试单元的功能,并提供更可靠的测试环境。
使用教程
引入Mockk
首先mockk仅支持JVM平台,如果在KMP中编写了通用的commonMain代码,那么它将无法工作。由于项目中KMP支持的平台有Android、iOS、PC Mac、Pc windows,而没有支持专门JVM平台,因此考虑将mockk放置到Android平台的androidUnitTest中,同时让Android的单元测试运行在JVM平台上。
在androidUnitTest源码集合添加依赖
备注:不要使用官网提示的testImplementation,KMP中没有这个方法支持
基本使用
- 在commonMain中编写产品类
- 在androidUnitTest中编写测试类
备注:其中every定义了mock对象mother的行为,当调用giveMoney时,返回值为30; verify用于校验kid对象的wantMoney是否被调用过。
参数匹配
备注:在定义mock对象行为时,可以进行参数匹配,此处使用了any()表明可以匹配任意的输入参数。
函数验证
备注:校验函数是否被调用过,前面的例子中已经写了。更高级的,还可以校验函数的调用次数、顺序、参数匹配等等。
备注:校验方法必须精确被调用10次
备注:校验调用顺序,firstMethod必须在secondMethod之前进行调用,否则验证不通过
偏函数模拟
备注:对于某些方法调用,我们并不想完全使用模拟的值,而是想使用特定的函数调用过程,那么可以使用originalCall来实现对实际函数的调用。
构造函数
备注:使用mockkConstructor方法mock构造函数,并通过anyConstructed进行类的构造,最后通过 unmockkConstructor取消构造函数的mock。
Lambada表达式
使用注解进行mock
备注:使用@MockK可以mock并注入一个对象,同时需要在@Before初始化函数中调用注入方法MockKAnnotations.init(this)
所有方法跳过准备
对于mock对象而言,其方法的调用都需要预设行为,否则会报错:io.mockk.MockKException: no answer found for: Car(car#1).getName()。如果我们不想对每个方法都预设,比如一个对象的方法实在太多了有上千个,那么我们可以使用以下三种方案,来取消对象的方法预设:
mock构造参数
mock注解指定
mock注解初始化
Unit方法跳过准备
返回值为Unit类型的跳过校验,而非Unit的方法不跳过校验
抓取参数
备注:首先在准备阶段创建了一个slot槽位,接着配合capture函数定义slot可以捕获到值。在执行阶段inform传入的参数,可以被slot捕获到,并存储在slot.captured的变量中。
静态方法
备注:实际上Java的类的静态方法也可以模拟,不过咱这里在KMP环境中只针对kotlin
静态对象
备注:模拟的如果是静态方法,那么参考13;模拟的如果是一个对象,那么使用mockObject即可
总结
MockK是一款功能强大、易于使用的Kotlin mocking框架,由于专门针对Kotlin进行设计,可以轻松的支持static方法、static类、final类mock。在Kotlin Multiplatform项目中,由于MockK不支持跨平台只支持JVM平台,因此需要将commonMain的测试代码,放置在可以运行于JVM虚拟机的源码集中。MockK的使用也比较简单,会使用Mokito的话很容易上手。下一篇将记录在KMP中的使用情况,以及自动构建需要如何配置。
最后
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。