鸿蒙掌上驾考宝典应用开发44:单元测试与 UI 测试——ohosTest 实践 第44篇单元测试与 UI 测试——ohosTest 实践一、引言在软件开发中测试是保证代码质量和功能正确性的关键手段。高效的测试体系不仅可以减少 Bug 逃逸还能在重构时提供安全网。DriverLicenseExam 项目在多个 HAR 模块中集成了 ohosTest 测试框架涵盖了单元测试和集成测试。本文将深入解析项目的测试架构和实践。二、ohosTest 测试框架概述2.1 测试框架架构ohosTest 是鸿蒙提供的官方测试框架支持以下测试类型ohosTest 测试体系 ├── 单元测试Unit Test │ ├── 纯逻辑测试不依赖 UI │ └── 数据服务测试 ├── 集成测试Integration Test │ ├── Ability 测试 │ └── 模块间交互测试 └── UI 测试UI Test ├── 组件渲染测试 └── 用户交互测试2.2 测试目录结构项目中每个 HAR 模块都包含测试目录commons/ ├── commonLib/ │ └── src/ │ ├── ohosTest/ets/test/ ← 集成测试运行在真机/模拟器 │ │ ├── Ability.test.ets │ │ └── List.test.ets │ └── test/ ← 单元测试可本地运行 │ ├── List.test.ets │ └── LocalUnit.test.ets ├── datasource/ │ └── src/ │ ├── ohosTest/ets/test/ │ │ ├── Ability.test.ets │ │ └── List.test.ets │ └── test/ │ ├── List.test.ets │ └── LocalUnit.test.ets └── network/ └── src/ ├── ohosTest/ets/test/ │ ├── Ability.test.ets │ └── List.test.ets └── test/ ├── List.test.ets └── LocalUnit.test.ets两个测试目录的区别目录运行环境测试类型适用场景src/test/本地 JVM单元测试纯逻辑、无 UI 依赖src/ohosTest/真机/模拟器集成测试依赖系统 API、UI三、单元测试实践3.1 测试框架初始化单元测试基于ohos/hypium测试框架使用describe和it组织测试用例// LocalUnit.test.ets import { describe, it, expect } from ohos/hypium; import { ExamService } from ohos_agcit/driver_license_exam_datasource; export default function examServiceTest() { describe(ExamServiceTest, () { // 测试用例写在这里 }); }3.2 数据服务测试ExamService 是应用的核心数据服务也是最值得编写测试的模块describe(ExamServiceTest, () { // 测试总题数 it(getTotalCount_should_return_correct_count, 0, () { const service ExamService.instance(getContext()); const count service.getTotalCount(); // 验证总题数是否正确 expect(count).assertEqual(10); }); // 测试正确率无已做题时应返回0 it(calAccuracyRate_should_return_zero_when_no_questions_done, 0, () { const service ExamService.instance(getContext()); const rate service.calAccuracyRate(); expect(rate).assertEqual(0); }); // 测试已做题数 it(getDidCount_should_return_number_of_answered_questions, 0, () { const service ExamService.instance(getContext()); const didCount service.getDidCount(); // 初始状态下应该为 0 expect(didCount).assertEqual(0); }); // 测试平均分无考试记录时应返回0 it(calculateAverageScore_should_return_zero_when_no_exams, 0, () { const service ExamService.instance(getContext()); const avgScore service.calculateAverageScore(); expect(avgScore).assertEqual(0); }); // 测试模拟考试记录 it(addMockExamCount_should_increase_count, 0, () { const service ExamService.instance(getContext()); const before service.mockExamCount; service.addMockExamCount(90); expect(service.mockExamCount).assertEqual(before 1); }); });每个测试用例的命名遵循methodName_expectedBehavior_when_condition模式让测试用例的意图一目了然。3.3 网络 Mock 测试网络层同样可以编写测试但由于网络请求依赖外部服务通常配合 Mock 使用describe(NetworkTest, () { it(mock_getUserInfo_should_return_mock_data, 0, () { const mockApi new HttpApiMock(); mockApi.getUserInfo().then((resp) { expect(resp.code).assertEqual(0); expect(resp.data.phone).assertEqual(1XXXXXX); }); }); });四、集成测试实践4.1 Ability 测试集成测试需要在真机或模拟器上运行验证 Ability 的生命周期和功能// Ability.test.ets import { describe, it, expect } from ohos/hypium; import { UIAbility } from kit.AbilityKit; describe(EntryAbilityTest, () { it(onWindowStageCreate_should_load_content_successfully, 0, () { // 验证 Ability 初始化成功 // 注意集成测试通常需要 Ability 测试框架的支持 }); it(setLightOrDarkMode_should_apply_correct_color_mode, 0, () { // 验证深色模式设置 }); });4.2 列表测试// List.test.ets describe(MainListTest, () { it(home_tab_should_display_correctly, 0, () { // 验证首页 Tab 是否正确显示 }); it(exam_list_should_render_questions, 0, () { // 验证考试列表是否正确渲染 }); });五、测试覆盖率分析5.1 建议测试覆盖的重点模块模块优先级测试重点ExamService 高数据统计、试卷生成、筛选逻辑AccountUtil 高登录/登出、信息管理PermissionUtil 中权限申请、状态检查RouterModule 中路由跳转、参数传递PreferencesUtil 低数据读写、缓存管理UI 组件 低渲染测试、交互测试5.2 边界情况测试好的测试不仅要覆盖正常路径更要覆盖边界情况describe(ExamServiceEdgeCases, () { it(calAccuracyRate_should_not_divide_by_zero, 0, () { const service ExamService.instance(getContext()); // 极端情况总题数为0 expect(service.calAccuracyRate()).assertEqual(0); }); it(calculateAverageScore_should_handle_empty_array, 0, () { const service ExamService.instance(getContext()); // 极端情况无考试记录 expect(service.calculateAverageScore()).assertEqual(0); }); });六、测试最佳实践6.1 AAA 模式每个测试用例都应该遵循 Arrange-Act-Assert 模式it(getCorrectCount_should_count_correct_answers, 0, () { // Arrange - 准备测试数据 const service ExamService.instance(getContext()); // Act - 执行测试操作标记一道题正确 service.examDetails[0].isCorrect true; // Assert - 验证结果 expect(service.getCorrectCount()).assertEqual(2); });6.2 测试隔离每个测试用例应该独立运行互不影响// 在每个测试之前重置状态 beforeEach(() { // 重新获取 ExamService 实例 }); // 避免测试用例之间共享可变状态 it(test1, () { ... }); it(test2, () { ... }); // 不依赖于 test1 的结果七、总结测试是工程化的重要组成部分。DriverLicenseExam 项目通过 ohosTest 框架构建了多层次的测试体系单元测试验证 ExamService、HttpApi 等核心服务的逻辑正确性集成测试验证 Ability 生命周期、模块间协作边界测试覆盖除零异常、空数据等极端情况测试体系的建立虽然需要投入额外的时间但它带来的收益是长期的回归保障修改代码时快速发现破坏性变更重构信心有测试覆盖的代码可以放心重构文档作用好的测试用例同时也是可执行的文档关键源码文件commons/commonLib/src/test/LocalUnit.test.ets— 单元测试示例commons/datasource/src/ohosTest/ets/test/Ability.test.ets— Ability 测试commons/network/src/test/List.test.ets— 列表测试

相关新闻

最新新闻

加特可逆势在华建CVT新厂:供应链韧性、技术迭代与市场基本盘保卫战

加特可逆势在华建CVT新厂:供应链韧性、技术迭代与市场基本盘保卫战

1. 项目背景:一个“迟到”的产能扩张决策 最近,汽车行业里一个不大不小的新闻引起了我的注意:加特可(JATCO)计划在中国建设第二家CVT(无级变速器)生产基地。乍一看,这似乎只是一个常…

2026/8/18 23:48:47
别克4月15日4款新车发布:新昂科拉回归与品牌战略转型分析

别克4月15日4款新车发布:新昂科拉回归与品牌战略转型分析

1. 从“4款新车”看别克的产品矩阵与市场策略 4月15日,别克要一口气发布4款新车,这消息一出,估计不少关注车市的朋友都跟我一样,心里咯噔一下:这是要放大招了?尤其是“新昂科拉”这个名字的出现&#xff0c…

2026/8/18 23:48:47
LLM智能体主动安全审计:基于轨迹-状态建模的事前预警实践

LLM智能体主动安全审计:基于轨迹-状态建模的事前预警实践

1. 从“事后灭火”到“事前预警”:为什么我们需要主动安全审计? 最近在折腾大语言模型驱动的多轮对话智能体时,我遇到了一个挺头疼的问题。你精心设计了一个客服机器人,让它能处理用户从咨询、投诉到售后跟进的一系列复杂对话。在…

2026/8/18 23:48:47
MySQL存储引擎深度对比:MyISAM与InnoDB的12个核心区别与选型指南

MySQL存储引擎深度对比:MyISAM与InnoDB的12个核心区别与选型指南

1. 项目概述:为什么我们还在讨论MyISAM和InnoDB? 如果你接触MySQL有一段时间了,尤其是在处理一些遗留系统或者阅读老的技术文档时,一定会反复遇到这两个名字:MyISAM和InnoDB。它们都是MySQL的存储引擎,你可…

2026/8/18 23:48:47
Spark Streaming微批处理架构解析与生产级应用实战指南

Spark Streaming微批处理架构解析与生产级应用实战指南

1. 项目概述:为什么Spark Streaming依然是实时计算的基石? 如果你正在处理海量的实时数据流,比如监控网站的用户点击行为、分析物联网设备的传感器读数,或者构建一个实时的推荐系统,那么“流处理”这个概念对你来说一定…

2026/8/18 23:48:47
好用还专业!2026年口碑不错的降AI率网站盘点

好用还专业!2026年口碑不错的降AI率网站盘点

论文送审前查了一遍AI疑似率,直接标红一片,导师消息发过来问“这部分是你自己写的吗”——这种场景这两年越来越常见。2026年高校对AI生成内容的检测标准持续收紧,降AI率已经从“可选项”变成了不少学生的“必选项”。市面上号称能降AI率的网…

2026/8/18 23:43:47