SuperAPI AI 工具集

mocking-with-mockito

测试与质量

查看项目

更新于 10-16 01:57

技能介绍

用途

本技能用于将 Mockito 接入 Android 模块的 JVM 单元测试套件。资料称 Mockito 是 androidx 自身使用的 mocking 框架,AOSP 检出中有 400 多个测试文件导入 org.mockito,而 io.mockk 没有命中;希望“与 Google 做法一致”的新 Android 代码会选择 Mockito 加 mockito-kotlin 的 Kotlin DSL。

主要能力与覆盖内容

资料说明该技能记录了精确的依赖矩阵、三种 setup 风格、Kotlin DSL 表面,以及在 Kotlin final class 场景下所需的 inline-mock-maker 配置。具体覆盖的场景包括:

  • 在 Kotlin Android 模块新建测试套件时选择 mocking 库。
  • Mockito 4.x 报出 Cannot mock/spy class … final class,需要 mock-maker-inline 配置。
  • 混用原始值与 any() / eq() 导致 InvalidUseOfMatchersException。
  • Kotlin 中 when 关键字冲突(when(mock.foo())),改用 whenever DSL。
  • 使用 argumentCaptor<T>() 的 firstValue / lastValue / allValues,而非 ArgumentCaptor.forClass(...)。
  • 从 MockitoAnnotations.initMocks(this) 迁移到 openMocks(this) 或 MockitoJUnit.rule()。
  • 在 API < 28 的仪器化测试中 mock final class。

使用方式

依赖方面,资料推荐 Mockito 5.x 作为基线,因为 inline mock-maker 默认启用,无需插件文件或替换 mockito-inline。示例坐标包括 testImplementation 的 org.mockito:mockito-core:5.14.2 与 org.mockito.kotlin:mockito-kotlin:5.4.0,以及 androidTestImplementation 的 org.mockito:mockito-android:5.14.2 与 org.mockito.kotlin:mockito-kotlin:5.4.0。资料强调 mockito-kotlin 不是 fork,而是直接委托给 mockito-core 的轻量 DSL 层,两者都必须在 classpath 上。

mock 生命周期有三种惯用方式:JUnit4 rule(首选,不占用 runner)、手动 openMocks(配合 @After 关闭返回的 AutoCloseable)、JUnit5 extension。资料明确不应使用已弃用的 initMocks。

Kotlin DSL 入口来自 org.mockito.kotlin.*,包括 mock、whenever、doReturn、any、eq、argumentCaptor、verify、atLeastOnce。资料列举的要点有:mock<T>() 是 reified 的,无需 T::class.java;mock { on { … } doReturn … } 是构造时桩化的构建块;whenever(...) 替代需要反引号的 when(...);argumentCaptor<T>() 提供 firstValue / secondValue / lastValue / allValues;any()、anyOrNull()、eq() 在 Kotlin 中为 null-safe。

匹配器规则方面,若任一参数使用匹配器,则所有参数都必须使用匹配器,否则抛出 InvalidUseOfMatchersException,原始值需用 eq(...) 包裹。

suspend 函数方面,Mockito 没有一等 suspend 支持,whenever(mock.suspendFn()) 要求调用点本身处于挂起上下文。资料建议用 runTest { ... }(首选)或 runBlocking(非 runTest 场景),但不可在 runTest 内嵌套 runBlocking。

对于 Mockito 4.x,需在 src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker 写入 mock-maker-inline;Mockito 5.x 默认启用。inline 仍有局限:final private 成员、equals()/hashCode()、String 和 Class 无法 mock。

对于 API < 28 的仪器化测试,资料说明 mockito-android 使用 dexmaker,仅 API 28+ 支持 final class mock,这是 dexmaker 约束而非 Mockito 版本约束。androidx 惯例是添加 @SdkSuppress(minSdkVersion = 28)。

资料还建议对新测试类应用 Strictness.STRICT_STUBS,通过 MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS) 接入,以暴露未使用的桩和参数不匹配。

适用场景与限制

适用:Kotlin Android 模块的 JVM 单元测试、需要与 androidx 一致的 mocking 方案、需要验证交互而非重新实现行为的场景。

资料明确指出不适用或应改用其他方案的情形:需要 coEvery / coVerify 处理 suspend、通过 mockkObject 深度单例 mock、或通过 mockkStatic 静态 mock 时,应使用 mocking-with-mockk;在设计层面选择 fake 还是 mock 时应参考 picking-test-doubles;runner/Gradle 矩阵未搭好时应先配置 JUnit4;需要 runTest 感知的协程测试时应参考相应协程技能。

资料还强调对带行为的协作者应优先使用 fake 而非 mock。

前置条件

资料列出的前置条件为:基础测试接线已就位(@RunWith(AndroidJUnit4::class)、androidx.test.ext:junit:1.3.0、JUnit 4.13.2);Kotlin 模块运行在 JDK 11+(Mockito 5.x 将 JDK 下限提高到 Java 11);若被测代码触及 Dispatchers.Main,需安装 MainDispatcherRule。

验证

资料给出了多条验证方式,包括运行 ./gradlew :<module>:testDebugUnitTest --tests <YourTestClass>、确认源码中没有 org.mockito.Mockito.when、MockitoAnnotations.initMocks、ArgumentCaptor.forClass(新代码)、确认 Mockito 4.x 项目存在对应 MockMaker 文件、已应用 STRICT_STUBS、final class 仪器化测试带有 @SdkSuppress(minSdkVersion = 28)。