Android 开发手册ANDROID DEV

CHAPTER 08质量保障 · TESTING

测试与调试

没有测试,重构就是赌博。本章覆盖 JUnit 单元/准基准测试、MockK 替身、Espresso/Compose UI 测试、基准测试与 Android Profiler 基础。

开始阅读难度中级约2小时
08.1

测试金字塔

大量快而廉价的单元测试打底,少量集成居中,更少但更慢的 UI 测试封顶。重心放单测,性价比最高。

Android 三层目录

test/ 本地 JVM 单测(快)、androidTest/ 设备 UI 测试(慢)、test/ 中可用 Robolectric 模拟框架。

08.2

JUnit 单元测试

用 @Test 标注测试方法,assertEquals 等断言验证行为。纯逻辑放在 test/ 下,毫秒级运行。

class CartTest {
    @Test fun add_increasesTotal() {
        val cart = Cart()
        cart.add(Product("A", 10)); cart.add(Product("B", 5))
        assertEquals(15, cart.total)
    }
}
08.3

MockK 替身

MockK 是 Kotlin 友好的 Mock 框架,支持 suspend、coroutine、私有方法,比 Mockito 更适合 Kotlin。

@MockK lateinit var repo: UserRepository
@RelaxedMockK lateinit var analytics: Analytics
@Before fun setup() = MockKAnnotations.init(this, relaxed = true)
@Test fun load_callsRepo() = runTest {
    coEvery { repo.users() } returns emptyList()
    val vm = HomeViewModel(repo, analytics)
    vm.load()
    coVerify { repo.users() }
}
08.4

ViewModel 测试

用 runTest + StandardTestDispatcher 控制协程调度,断言 StateFlow 取值。

@Test fun login_success_setsState() = runTest {
    val vm = LoginViewModel(fakeRepo)
    vm.login("u", "p")
    assertTrue(vm.state.value is LoginState.Success)
}
08.5

UI 测试

Espresso 测 View 系统、Compose UI Test 测 Compose,通过资源 id 模拟点击并断言。

composeTestRule.setContent { Greeting("X") }
composeTestRule.onNodeWithText("X").assertIsDisplayed()
UI 测试慢

启动真机级 App,分钟级。只在 CI 跑核心链路,细节交给单测。

08.6

基准测试

Macrobenchmark 测量启动、滚动帧率;Microbenchmark 测量小函数耗时,防回归。

本地先绿再提交

CI 是最后闸,先本地跑通相关测试再把反馈循环缩到最短。

08.7

Profiler 调试

Android Studio Profiler 看 CPU、内存、网络、能耗。卡顿用 CPU 记录,泄漏用内存堆转储(HPROF)。

08.8

常见陷阱

单测为什么慢?

多因触碰真实网络/数据库或启动 App 生命周期。用 MockK 替换外部依赖,把被测单元缩到纯逻辑。

主线程断言协程结果报 Exception?

测试协程需用 runTest 与 TestDispatcher,否则默认调度器会抛主线程限制异常。

08.9

实战练习

延伸资源