【句匠|19】HarmonyOS ArkTS 回归测试实战:覆盖启动、空数据、异常输入和重复点击

发布时间:2026/9/8 19:25:53
【句匠|19】HarmonyOS ArkTS 回归测试实战:覆盖启动、空数据、异常输入和重复点击 HarmonyOS 应用能通过编译只能证明语法、类型和资源引用满足构建要求能打开首屏也不能证明空数据、异常路由参数、重复点击、窗口缩放、数据写入失败和页面二次进入都不会出错。对学习类应用来说真正影响审核和用户体验的往往不是主流程而是“第二次执行同一个动作”或“数据恰好为空”。本文基于句匠项目D:\huawei\one18-11的真实源码核对entry/src/test、entry/src/ohosTest、EntryAbility.ets、AICorrectPage.ets、BankDetailPage.ets、CategoryPage.ets、DailyCheckInPage.ets、ExamResultPage.ets、PracticePage.ets与UserDataManager.ets。本文唯一源码标识com.jiaweikang.one18。真实边界必须先说明工程已经引入ohos/hypium与ohos/hamock入口和两个库模块也有src/test、src/ohosTest目录但现有测试主要是 DevEco 模板只断言字符串abc包含b没有覆盖业务页面、数据服务、路由参数或重复操作。本文给出的是基于源码风险设计的回归方案不声称这些用例已经自动执行、真机通过或进入 CI。一、先把“测试存在”和“业务有覆盖”分开entry/src/test/LocalUnit.test.ets当前核心用例是it(assertContain, 0, () { let a abc let b b expect(a).assertContain(b) expect(a).assertEqual(a) })entry/src/ohosTest/ets/test/Ability.test.ets也是相同模板。它能证明测试框架入口可被组织但不能证明句匠的任何功能正确。业务回归覆盖至少需要回答测试对象是谁 输入状态是什么 执行什么动作 期望页面、内存和磁盘如何变化 失败时如何定位 是否在目标设备上执行如果这些问题没有答案就不能把“有 test 目录”写成“测试体系完善”。二、从真实风险建立分层测试金字塔句匠适合分为四层层级目标示例纯逻辑单测快速验证确定性函数纠错规则、日期差、统计、格式化服务单测验证状态转换与存储收藏切换、清空、进度更新页面/组件测试验证状态与交互空输入、空题库、按钮禁用设备回归验证系统环境启动、TTS、窗口缩放、安全区纯逻辑用例数量可以多运行频繁设备回归数量少但必须覆盖发布阻断路径。不能只靠一端。三、启动链路是第一条阻断用例EntryAbility.onCreate()会设置浅色模式初始化UserDataManager创建多个AppStorage键注册BreakpointSystem。onWindowStageCreate()获取主窗口、读取安全区、注册监听然后加载pages/SplashPage。启动回归至少包含首次安装后启动 已有本地数据后启动 Preferences 内容异常后启动 主窗口避让区读取失败后启动 SplashPage 加载失败日志检查 后台返回前台 销毁后重新进入源码在 Preferences 初始化失败时会创建空数组和默认设置因此“损坏数据不白屏”是可验证目标而不是默认认为一定成功。四、空数据不只是显示一张空状态图BankDetailPage在bank undefined时展示“未找到题库”。这条分支会在路由参数缺失、bankId不存在或固定题库映射失败时触发。需要验证无 params 进入 params 为空对象 bankId 为空字符串 bankId 不存在 bankId 类型异常 正常 bankId期望是页面可返回、不崩溃、不调用this.bank!的后续逻辑。空状态上的返回操作也要实际可达。五、AI 纠错页适合先做纯逻辑单测AICorrectPage内部有多条确定性正则规则例如I very like → like ... very much I no idea → I have no idea interested on → interested in recieve → receive I yesterday go → I went yesterday页面对空输入有明确判断if (text.trim().length 0) { this.results [] this.analyzed true return }建议把规则匹配从页面抽到纯函数再覆盖空字符串 全空格 正常无错误句子 单个错误 同一句多个错误 大小写差异 错误词位于标点前 极长输入 重复点击分析 清空后再次分析当前规则定义在页面文件中尚未拆成可直接导入的测试模块这是自动化前需要做的最小结构调整。六、空输入与“没有发现错误”必须区分空输入和正确句子都可能得到results.length 0但产品语义不同空输入用户没有提供可分析内容正确句子已经分析但没有命中规则。源码使用analyzed状态控制结果区。回归需要确认空输入不会误导用户为“句子完全正确”正确句子则展示明确的无错误结果。状态表可以写成inputanalyzedresults期望空false0初始输入态空格true0空输入提示/不误判正确句true0无明显错误错误句true0建议列表七、每日打卡重点测重复点击DailyCheckInPage.checkIn()首先判断if (this.isCheckedToday()) { this.message 今天已经打卡啦 return }这是典型幂等保护。回归需要执行当天无记录 → 点击一次 当天已记录 → 再点击 快速连续点击两次 跨天后点击 记录只有一天 记录连续两天 记录中存在重复日期期望当天日期只出现一次积分与累计次数不重复增加提示文本与按钮状态一致。仅看代码中的return不够。ArkUI 事件在快速点击下是否会在状态更新前重复进入还需真机或组件测试验证。八、日期逻辑要测月末、年末与时区源码将日期格式化为本地YYYY-MM-DD并通过毫秒差计算天数。普通日期容易通过边界包括1月31日 → 2月1日 2月28/29日 → 3月1日 12月31日 → 1月1日 夏令时地区跨日 设备时间被手动修改 记录顺序被打乱如果测试直接使用new Date()用例会随执行日期变化。更可测的设计是把“当前时间”作为参数注入纯函数。九、ExamResultPage 存在重复写入风险ExamResultPage.aboutToAppear()读取路由参数并在考试模式下追加历史this.examHistory UserDataManager.addExamHistory( this.examHistory, history )如果同一页面因为生命周期再次进入aboutToAppear()可能重复追加同一场考试。源码没有可见的结果唯一 ID 或“已保存”标记。必须覆盖考试结束首次进入 切后台再回来 跳转其他页后返回 旋转/窗口变化 快速点击查看结果 结果页重复构建如果历史增加两条就是实际缺陷。解决方案可以是生成 attemptId 并去重或页面内保存一次性标记但本文不声称已经修复。十、异常路由参数不能依赖类型断言源码常见写法const params router.getParams() as ExamResultParams | undefinedas只改变编译器视角不会在运行时验证字段。测试要传入undefined {} 缺少 bankId score 为字符串 records 不是数组 负数 durationSec correct total 超长 bankId页面应采用默认值、空状态或返回路径而不是崩溃。真正严格的路由参数需要运行时守卫不是更复杂的 TypeScript 接口。十一、收藏、笔记和错题要测状态闭环UserDataManager的操作通常返回新数组页面重新赋值给StorageLink。一条完整用例包含初始内存状态 执行保存/删除 返回数组 AppStorage 页面变化 Preferences 刷盘 重启恢复收藏切换应覆盖空数组新增 已有记录删除 不同题库同题号 重复快速点击 写盘失败 清空后重启当前persist()静默吞掉异常因此写盘失败的可观察性不足。自动化可以通过存储适配器注入失败验证页面是否需要错误状态。十二、清除全部数据要测部分失败设置页提供二次确认清除但底层是多个键分别写入并非事务。测试不能只检查“按钮变成已清除”还要检查每个键favoriteRecords noteRecords wrongRecords bankProgress chapterProgress examHistory dailyReminderTime examDurationSec autoNextQuestion当第三个键写入失败时页面是否显示成功重启后哪些数据回来这些问题只有故障注入才能回答。十三、TTS 回归要覆盖能力降级练习页优先离线 TTS失败后尝试在线引擎并始终展示发音弹窗。设备测试矩阵包括场景预期离线引擎可用正常朗读离线失败、在线可用在线兜底完全无网络弹窗和文本仍可用引擎忙碌停止前一次再朗读快速重复播放不叠音、不崩溃离开页面stop shutdown返回页面再播放可重新初始化这类能力依赖设备服务纯单测不能替代真机验证。十四、响应式布局要测临界宽度句匠定义sm/md/lg部分页面又以 700vp 或 720vp 切换网格。回归点不应只选 360vp 和 1200vp还要紧贴边界599 / 600 / 601vp 839 / 840 / 841vp 699 / 700 / 701vp 719 / 720 / 721vp验证题库列表列数、详情页双栏、分类页卡片宽度、滚动区域和长标题。前文还发现Index.ets的根导航条件覆盖全部合法断点侧边栏分支不可达这应成为明确回归用例。十五、重复点击要按操作类型分类并不是所有按钮都用同一种防抖操作风险策略打卡重复记账幂等判断提交考试重复生成结果提交锁 attemptId保存笔记高频写盘合并/保存中状态播放 TTS多路叠音stop 前一次路由跳转重复页面点击锁或路由策略清除数据破坏性重复操作二次确认 结果校验测试要验证业务结果而不是只验证按钮暂时变灰。十六、建议先抽离五类纯函数当前很多逻辑位于页面结构体内部。优先抽离analyzeSentence(text) calculateStreak(records, now) normalizeExamParams(raw) selectLayout(bp, width) buildExamHistory(params)纯函数输入输出明确能快速覆盖异常值和边界值。页面测试再只关注状态绑定、显示与点击。示例测试结构it(empty input returns no suggestions, 0, () { const result analyzeSentence( ) expect(result.length).assertEqual(0) })这只是建议代码当前工程没有该函数。十七、测试数据必须稳定且可重置回归测试最怕共享真实 Preferences。上一个用例留下的收藏和考试记录会污染下一个用例。每个用例应明确Arrange准备独立存储或内存仓库 Act执行一次操作 Assert检查返回值、AppStorage和持久层 Cleanup恢复初始状态设备测试可使用专门测试包、测试存储名称或测试前清除应用数据。不要把用户真实学习数据作为测试输入。十八、发布前烟测必须使用候选包DevEco Preview 或单元测试通过不能替代发布候选包安装 首次启动 进入主页 打开题库详情 完成一道练习 触发听音题 收藏与笔记 完成考试 查看结果和历史 清除数据 切换窗口尺寸 退出 卸载需要记录实际包版本、设备类型、系统版本、结果和证据。未执行就标记not run不能写成通过。十九、把回归结果变成发布门槛建议使用三种结果passed已执行且符合预期 failed已执行且发现问题 not run未执行或环境不具备发布阻断项包括启动崩溃、核心流程不可达、数据重复写入、清除失效、权限循环、关键布局遮挡和候选包无法安装。非阻断视觉问题也要记录但不能掩盖核心失败。二十、结语从模板测试走向风险测试句匠已经有 Hypium、Hamock 和测试目录但当前内容仍是模板断言不能证明业务回归覆盖。真实源码已经提供了清晰的测试入口启动初始化与容错、AI纠错空输入、题库详情异常参数、每日打卡幂等、考试结果重复写入、本地状态闭环、TTS能力降级和多设备临界宽度。高价值回归测试不是追求用例数量而是把最可能导致崩溃、重复数据、错误状态和审核失败的风险固定下来。先抽离纯逻辑再测试服务状态转换随后覆盖页面交互最后用发布候选包完成安装—启动—核心流程—卸载烟测。在这些测试真正执行并留下证据之前工程只能说“已有测试框架和回归设计”不能说“已通过完整自动化测试”。这种边界意识本身就是可靠发布流程的一部分。本文部分内容由 AI 辅助整理测试现状、风险点与用例均来自句匠真实源码未虚构测试通过率、设备结果、审核结论或线上数据。