Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

测试体系

★ 测试体系是中级 Android 面试的短板高频区。能讲清“怎么测“,比只会说 MVVM/MVI 更能证明工程能力。

一、测试金字塔与 Android 测试分层

层级目标工具典型对象
单元测试快速验证纯逻辑JUnit / Truth / MockKUseCase、Repository、Reducer
集成测试验证多层协作Robolectric / fake data sourceViewModel + Repository
UI 测试验证用户路径Espresso / Compose Test页面交互、导航、错误提示

原则:越靠下越多、越快、越稳定;越靠上越少、越接近真实用户。

二、单元测试:Junit、断言与 Mock

  • JUnit 负责组织测试生命周期。
  • Truth/AssertJ 让断言可读。
  • MockK/Mockito 用于隔离外部依赖,但不要 mock 一切。
class LoginUseCaseTest {
    @Test
    fun `blank username returns validation error`() {
        val useCase = LoginUseCase(fakeRepository)
        val result = useCase.execute(username = "", password = "123456")
        assertThat(result).isEqualTo(LoginResult.InvalidUsername)
    }
}

三、协程与 Flow 测试

  • runTest 控制虚拟时间。
  • StandardTestDispatcher 替换真实 dispatcher。
  • Flow 可用 Turbine 验证 emit 顺序。
@Test
fun `flow emits loading then success`() = runTest {
    repository.userFlow().test {
        assertThat(awaitItem()).isEqualTo(UiState.Loading)
        assertThat(awaitItem()).isInstanceOf(UiState.Success::class.java)
        awaitComplete()
    }
}

四、ViewModel / Repository 怎么测

ViewModel 测试重点不是测试 Android 框架,而是测试输入事件到 UI State 的转换。

对象测什么不测什么
ViewModelstate/effect、错误处理、重试具体控件绘制
Repository缓存策略、数据源切换Retrofit/Room 本身
UseCase业务规则外部 IO

五、UI 测试:Espresso 与 Compose Test

  • Espresso 适合 View 体系页面。
  • Compose Test 适合声明式 UI,优先通过 semantic matcher 找节点。
  • UI 测试要覆盖核心路径,不要把所有边界都堆在 UI 层。

六、可测试性如何反推架构质量

如果一个 ViewModel 很难测,通常说明它持有太多 Android 依赖、业务逻辑没有下沉、状态/副作用没有分离。

高频面试题

Q1:你项目里怎么做测试分层? 答:核心业务规则放单元测试,ViewModel/Repository 做集成测试,关键用户路径做 UI 测试。比例上单元测试最多,UI 测试最少。

Q2:为什么 Repository 不应该直接测 Retrofit/Room? 答:Retrofit/Room 是框架能力,业务测试应关注 Repository 的缓存、错误兜底、数据源切换。框架集成可少量用 integration test 验证。

Q3:协程测试为什么不用真实 delay? 答:真实 delay 会让测试慢且不稳定;runTest 用虚拟时间推进,可稳定验证超时、重试、debounce 等逻辑。

易错点 / 追问

  • 不要为了覆盖率 mock 所有东西,那会测到实现细节。
  • UI 测试不要依赖真实网络和随机数据。
  • Compose 测试要给关键节点加稳定语义,否则 matcher 容易脆弱。