【Jetpack Compose进阶学与练】第19课 单元测试与UI测试;ViewModel单元测试 + Compose界面测试基础
前言前面课程主要聚焦业务开发软件项目开发离不开测试。本节课学习区分单元测试(JVM本地) 和Instrumented仪器化UI测试(Android设备/模拟器)ViewModel单元测试Compose的界面测试基础使用test‑junit4以及compose‑testing。知识点清单区分单元测试test目录JVM运行无设备 vs 仪器化测试(androidTest目录模拟器/真机执行)ViewModel单元测试测试业务逻辑、状态流转使用Mock模拟仓库Compose UI测试ComposeTestRule节点查找、断言、模拟点击测试中隔离业务避免真实网络请求简单测试用例示例build.gradle 依赖testImplementation junit:junit:4.13.2 testImplementation org.mockito:mockito-core:5.14.2 androidTestImplementation androidx.test.ext:junit:1.2.1 androidTestImplementation androidx.compose.ui:ui-test-junit4:1.7.3 debugImplementation androidx.compose.ui:ui-test-manifest:1.7.3完整示例源码importandroidx.lifecycle.ViewModelimportandroidx.lifecycle.viewModelScopeimportkotlinx.coroutines.flow.MutableStateFlowimportkotlinx.coroutines.flow.StateFlowimportkotlinx.coroutines.launch//业务ViewModel计数器示例classCounterViewModel:ViewModel{privateval_countMutableStateFlow(0)valcount:StateFlowInt_countfunincrease(){viewModelScope.launch{_count.value_count.value1}}fundecrease(){viewModelScope.launch{_count.value_count.value-1}}}/** * ViewModel本地单元测试放在 test/ 目录JVM不需要模拟器 */importkotlinx.coroutines.test.runTestimportorg.junit.Assert.assertEqualsimportorg.junit.TestclassCounterViewModelTest{TestfuntestIncrease()runTest{valvmCounterViewModel()vm.increase()assertEquals(1,vm.count.value)}TestfuntestDecrease()runTest{valvmCounterViewModel()vm.decrease()assertEquals(-1,vm.count.value)}}/** * Compose UI仪器化测试示例androidTest目录模拟器运行 */importandroidx.compose.ui.test.junit4.createComposeRuleimportandroidx.compose.ui.test.onNodeWithTextimportandroidx.compose.ui.test.performClickimportorg.junit.Ruleimportorg.junit.TestclassCounterScreenUiTest{get:RulevalcomposeTestRulecreateComposeRule()TestfunuiCounter_clickAdd_increaseNumber(){//挂载Compose页面composeTestRule.setContent{//此处挂载对应的Counter UI页面}//模拟点击“”按钮composeTestRule.onNodeWithText().performClick()//断言界面上文本显示数字1composeTestRule.onNodeWithText(1).assertExists()}}逐行解读两套测试目录区别src/test 本地单元测试直接跑在电脑JVM上不需要Android设备适合ViewModel、Repository工具类业务逻辑但是不能调用Android系统SDKAPIAndroid框架类需要mock。src/androidTest 仪器化测试运行于模拟器或者真机上面可以真实执行Compose渲染用于UI自动化测试可以查找界面节点、模拟点击、输入文本、做界面断言。ViewModel单元测试要点ViewModel里面大量使用协程 viewModelScope 正式单元测试的时候要用 runTest 替换真实的Android主调度器runTest是kotlin‑coroutines‑test提供虚拟时间快进协程不需要sleep等待。测试原则尽量隔离不要访问真实网络、真实数据库使用mock模拟仓库返回数据保证用例稳定不受后端环境影响。Compose UI测试核心 ComposeTestRuleget:Rule val composeTestRule createComposeRule()setContent{}把要测试的Compose组件挂载到测试环境onNodeWithText(“”)通过文本找到UI上组件节点performClick()模拟用户点击assertExists() / assertDoesNotExist()界面断言判断该UI节点是否出现也可以使用 onNodeWithTag 给组件设置 Modifier.testTag(“btn_add”) 优先使用testTag避免后续修改文字导致测试用例失效。test‑manifestdebugImplementationui‑test‑manifest 只在debug编译生效提供Compose测试所需要的Activity不要加到release依赖打包会报错。测试的策略建议分层测试思路①优先写ViewModel业务逻辑单元测试test目录速度很快②Repository层测试③少量关键页面流程补充Compose UI仪器化测试UI测试运行速度慢模拟器开销大不要把全部测试都写UI自动化重点业务流程覆盖即可。容易踩坑❌ 在test本地单元测试直接调用Android SDK类ContextToast会直接抛异常本地JVM没有Android框架。❌ release版本引入ui‑test‑manifest打包编译报错必须是debugImplementation。❌ UI测试大量依靠文本查找节点后续产品修改按钮文字直接导致全部UI用例失效优先使用Modifier.testTag标记控件。❌ 测试用例里面写Thread.sleep()等待网络应当mock Repository返回假数据消除网络等待。✅ test目录ViewModel业务单元测试(JVM)androidTest目录Compose界面仪器化测试runTest虚拟协程时间优先testTag定位UI节点mock隔离外部IO与网络。多元化习题练习习题1【基础‑改错题】想在src/test目录单元测试里面直接调用Toast.makeText()运行直接报错为什么答案src/test运行环境是电脑本地JVM没有Android系统框架不能直接使用Android SDK API如果必须要需要mock模拟。解读本地单元测试适合纯kotlin业务逻辑Android系统相关逻辑放到仪器化测试。习题2【基础‑代码修改题】给一个Button设置测试标记testTag用于UI查找节点只写Modifier片段答案Modifier.testTag(btn_increase)解读测试代码使用onNodeWithTag(“btn_increase”)查找该控件不受按钮显示文字改动影响。习题3【进阶‑补充代码题】ViewModel测试当中runTest{}作用是什么答案runTest提供虚拟协程调度器接管协程执行可以快进虚拟时间不需要真实sleep等待延时任务专门用于协程相关单元测试。解读viewModelScope内部启动的协程在runTest环境下可以被测试框架管控。习题4【综合‑逻辑应用题】项目当中为什么不要全部写UI自动化测试优先ViewModel单元测试答案ViewModel单元测试跑于本地JVM速度快不需要模拟器稳定性好适合反复回归业务逻辑UI仪器化测试需要真机/模拟器运行速度慢页面改动之后容易导致测试脚本失效所以优先对业务逻辑层写单元测试UI自动化只覆盖少量核心业务流程。解读分层测试底层逻辑优先上层UI为辅。习题5【概念简答题】mock模拟Repository层返回数据主要目的是什么答案隔离真实后端网络测试不再依赖后端服务不管后端是否可用测试用例都可以稳定执行可以方便构造成功、失败、异常多种返回场景。解读解除外部环境依赖是单元测试的关键思想。本节课知识点总结两个测试目录src/test本地JVM单元测试src/androidTest真机/模拟器仪器化UI测试。ViewModel协程业务逻辑单元测试使用runTest虚拟协程时间尽量mock仓库隔离网络与IO。Compose UI测试依靠ComposeTestRulesetContent挂载组件查找节点模拟点击界面断言优先使用testTag不依赖显示文本。ui‑test‑manifest只能debugImplementation不能release打包引入。测试分层策略优先ViewModel、Repository业务单元测试少量核心流程补充UI自动化测试不要全部依赖UI测试。下一课预告第20课 KMP Compose‑Multiplatform基础一套UI跨Android、桌面