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

HarmonyOS 应用能通过编译,只能证明语法、类型和资源引用满足构建要求;能打开首屏,也不能证明空数据、异常路由参数、重复点击、窗口缩放、数据写入失败和页面二次进入都不会出错。对学习类应用来说,真正影响审核和用户体验的往往不是主流程,而是"第二次执行同一个动作"或"数据恰好为空"。

本文基于句匠项目 D:\huawei\one18-11 的真实源码,核对 entry/src/testentry/src/ohosTestEntryAbility.etsAICorrectPage.etsBankDetailPage.etsCategoryPage.etsDailyCheckInPage.etsExamResultPage.etsPracticePage.etsUserDataManager.ets

本文唯一源码标识:com.jiaweikang.one18

真实边界必须先说明:工程已经引入 @ohos/hypium@ohos/hamock,入口和两个库模块也有 src/testsrc/ohosTest 目录;但现有测试主要是 DevEco 模板,只断言字符串 abc 包含 b,没有覆盖业务页面、数据服务、路由参数或重复操作。本文给出的是基于源码风险设计的回归方案,不声称这些用例已经自动执行、真机通过或进入 CI。

一、先把"测试存在"和"业务有覆盖"分开

entry/src/test/LocalUnit.test.ets 当前核心用例是:

ts 复制代码
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 也是相同模板。它能证明测试框架入口可被组织,但不能证明句匠的任何功能正确。

业务回归覆盖至少需要回答:

text 复制代码
测试对象是谁?
输入状态是什么?
执行什么动作?
期望页面、内存和磁盘如何变化?
失败时如何定位?
是否在目标设备上执行?

如果这些问题没有答案,就不能把"有 test 目录"写成"测试体系完善"。

二、从真实风险建立分层测试金字塔

句匠适合分为四层:

层级 目标 示例
纯逻辑单测 快速验证确定性函数 纠错规则、日期差、统计、格式化
服务单测 验证状态转换与存储 收藏切换、清空、进度更新
页面/组件测试 验证状态与交互 空输入、空题库、按钮禁用
设备回归 验证系统环境 启动、TTS、窗口缩放、安全区

纯逻辑用例数量可以多,运行频繁;设备回归数量少,但必须覆盖发布阻断路径。不能只靠一端。

三、启动链路是第一条阻断用例

EntryAbility.onCreate() 会:

  1. 设置浅色模式;
  2. 初始化 UserDataManager
  3. 创建多个 AppStorage 键;
  4. 注册 BreakpointSystem

onWindowStageCreate() 获取主窗口、读取安全区、注册监听,然后加载 pages/SplashPage

启动回归至少包含:

text 复制代码
首次安装后启动
已有本地数据后启动
Preferences 内容异常后启动
主窗口避让区读取失败后启动
SplashPage 加载失败日志检查
后台返回前台
销毁后重新进入

源码在 Preferences 初始化失败时会创建空数组和默认设置,因此"损坏数据不白屏"是可验证目标,而不是默认认为一定成功。

四、空数据不只是显示一张空状态图

BankDetailPagebank === undefined 时展示"未找到题库"。这条分支会在路由参数缺失、bankId 不存在或固定题库映射失败时触发。

需要验证:

text 复制代码
无 params 进入
params 为空对象
bankId 为空字符串
bankId 不存在
bankId 类型异常
正常 bankId

期望是页面可返回、不崩溃、不调用 this.bank! 的后续逻辑。空状态上的返回操作也要实际可达。

五、AI 纠错页适合先做纯逻辑单测

AICorrectPage 内部有多条确定性正则规则,例如:

text 复制代码
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

页面对空输入有明确判断:

ts 复制代码
if (text.trim().length === 0) {
  this.results = []
  this.analyzed = true
  return
}

建议把规则匹配从页面抽到纯函数,再覆盖:

text 复制代码
空字符串
全空格
正常无错误句子
单个错误
同一句多个错误
大小写差异
错误词位于标点前
极长输入
重复点击分析
清空后再次分析

当前规则定义在页面文件中,尚未拆成可直接导入的测试模块,这是自动化前需要做的最小结构调整。

六、空输入与"没有发现错误"必须区分

空输入和正确句子都可能得到 results.length === 0,但产品语义不同:

  • 空输入:用户没有提供可分析内容;
  • 正确句子:已经分析,但没有命中规则。

源码使用 analyzed 状态控制结果区。回归需要确认空输入不会误导用户为"句子完全正确",正确句子则展示明确的无错误结果。

状态表可以写成:

input analyzed results 期望
false 0 初始输入态
空格 true 0 空输入提示/不误判
正确句 true 0 无明显错误
错误句 true >0 建议列表

七、每日打卡重点测重复点击

DailyCheckInPage.checkIn() 首先判断:

ts 复制代码
if (this.isCheckedToday()) {
  this.message = '今天已经打卡啦'
  return
}

这是典型幂等保护。回归需要执行:

text 复制代码
当天无记录 → 点击一次
当天已记录 → 再点击
快速连续点击两次
跨天后点击
记录只有一天
记录连续两天
记录中存在重复日期

期望当天日期只出现一次,积分与累计次数不重复增加,提示文本与按钮状态一致。

仅看代码中的 return 不够。ArkUI 事件在快速点击下是否会在状态更新前重复进入,还需真机或组件测试验证。

八、日期逻辑要测月末、年末与时区

源码将日期格式化为本地 YYYY-MM-DD,并通过毫秒差计算天数。普通日期容易通过,边界包括:

text 复制代码
1月31日 → 2月1日
2月28/29日 → 3月1日
12月31日 → 1月1日
夏令时地区跨日
设备时间被手动修改
记录顺序被打乱

如果测试直接使用 new Date(),用例会随执行日期变化。更可测的设计是把"当前时间"作为参数注入纯函数。

九、ExamResultPage 存在重复写入风险

ExamResultPage.aboutToAppear() 读取路由参数,并在考试模式下追加历史:

ts 复制代码
this.examHistory =
  UserDataManager.addExamHistory(
    this.examHistory,
    history
  )

如果同一页面因为生命周期再次进入 aboutToAppear(),可能重复追加同一场考试。源码没有可见的结果唯一 ID 或"已保存"标记。

必须覆盖:

text 复制代码
考试结束首次进入
切后台再回来
跳转其他页后返回
旋转/窗口变化
快速点击查看结果
结果页重复构建

如果历史增加两条,就是实际缺陷。解决方案可以是生成 attemptId 并去重,或页面内保存一次性标记,但本文不声称已经修复。

十、异常路由参数不能依赖类型断言

源码常见写法:

ts 复制代码
const params =
  router.getParams() as ExamResultParams | undefined

as 只改变编译器视角,不会在运行时验证字段。测试要传入:

text 复制代码
undefined
{}
缺少 bankId
score 为字符串
records 不是数组
负数 durationSec
correct > total
超长 bankId

页面应采用默认值、空状态或返回路径,而不是崩溃。真正严格的路由参数需要运行时守卫,不是更复杂的 TypeScript 接口。

十一、收藏、笔记和错题要测状态闭环

UserDataManager 的操作通常返回新数组,页面重新赋值给 @StorageLink。一条完整用例包含:

text 复制代码
初始内存状态
执行保存/删除
返回数组
AppStorage 页面变化
Preferences 刷盘
重启恢复

收藏切换应覆盖:

text 复制代码
空数组新增
已有记录删除
不同题库同题号
重复快速点击
写盘失败
清空后重启

当前 persist() 静默吞掉异常,因此写盘失败的可观察性不足。自动化可以通过存储适配器注入失败,验证页面是否需要错误状态。

十二、清除全部数据要测部分失败

设置页提供二次确认清除,但底层是多个键分别写入,并非事务。

测试不能只检查"按钮变成已清除",还要检查每个键:

text 复制代码
favoriteRecords
noteRecords
wrongRecords
bankProgress
chapterProgress
examHistory
dailyReminderTime
examDurationSec
autoNextQuestion

当第三个键写入失败时,页面是否显示成功?重启后哪些数据回来?这些问题只有故障注入才能回答。

十三、TTS 回归要覆盖能力降级

练习页优先离线 TTS,失败后尝试在线引擎,并始终展示发音弹窗。设备测试矩阵包括:

场景 预期
离线引擎可用 正常朗读
离线失败、在线可用 在线兜底
完全无网络 弹窗和文本仍可用
引擎忙碌 停止前一次再朗读
快速重复播放 不叠音、不崩溃
离开页面 stop + shutdown
返回页面再播放 可重新初始化

这类能力依赖设备服务,纯单测不能替代真机验证。

十四、响应式布局要测临界宽度

句匠定义 sm/md/lg,部分页面又以 700vp 或 720vp 切换网格。回归点不应只选 360vp 和 1200vp,还要紧贴边界:

text 复制代码
599 / 600 / 601vp
839 / 840 / 841vp
699 / 700 / 701vp
719 / 720 / 721vp

验证题库列表列数、详情页双栏、分类页卡片宽度、滚动区域和长标题。前文还发现 Index.ets 的根导航条件覆盖全部合法断点,侧边栏分支不可达,这应成为明确回归用例。

十五、重复点击要按操作类型分类

并不是所有按钮都用同一种防抖:

操作 风险 策略
打卡 重复记账 幂等判断
提交考试 重复生成结果 提交锁 + attemptId
保存笔记 高频写盘 合并/保存中状态
播放 TTS 多路叠音 stop 前一次
路由跳转 重复页面 点击锁或路由策略
清除数据 破坏性重复操作 二次确认 + 结果校验

测试要验证业务结果,而不是只验证按钮暂时变灰。

十六、建议先抽离五类纯函数

当前很多逻辑位于页面结构体内部。优先抽离:

text 复制代码
analyzeSentence(text)
calculateStreak(records, now)
normalizeExamParams(raw)
selectLayout(bp, width)
buildExamHistory(params)

纯函数输入输出明确,能快速覆盖异常值和边界值。页面测试再只关注状态绑定、显示与点击。

示例测试结构:

ts 复制代码
it('empty input returns no suggestions', 0, () => {
  const result = analyzeSentence('   ')
  expect(result.length).assertEqual(0)
})

这只是建议代码,当前工程没有该函数。

十七、测试数据必须稳定且可重置

回归测试最怕共享真实 Preferences。上一个用例留下的收藏和考试记录会污染下一个用例。

每个用例应明确:

text 复制代码
Arrange:准备独立存储或内存仓库
Act:执行一次操作
Assert:检查返回值、AppStorage和持久层
Cleanup:恢复初始状态

设备测试可使用专门测试包、测试存储名称或测试前清除应用数据。不要把用户真实学习数据作为测试输入。

十八、发布前烟测必须使用候选包

DevEco Preview 或单元测试通过,不能替代发布候选包:

text 复制代码
安装
首次启动
进入主页
打开题库详情
完成一道练习
触发听音题
收藏与笔记
完成考试
查看结果和历史
清除数据
切换窗口尺寸
退出
卸载

需要记录实际包版本、设备类型、系统版本、结果和证据。未执行就标记 not run,不能写成通过。

十九、把回归结果变成发布门槛

建议使用三种结果:

text 复制代码
passed:已执行且符合预期
failed:已执行且发现问题
not run:未执行或环境不具备

发布阻断项包括启动崩溃、核心流程不可达、数据重复写入、清除失效、权限循环、关键布局遮挡和候选包无法安装。非阻断视觉问题也要记录,但不能掩盖核心失败。

二十、结语:从模板测试走向风险测试

句匠已经有 Hypium、Hamock 和测试目录,但当前内容仍是模板断言,不能证明业务回归覆盖。真实源码已经提供了清晰的测试入口:启动初始化与容错、AI纠错空输入、题库详情异常参数、每日打卡幂等、考试结果重复写入、本地状态闭环、TTS能力降级和多设备临界宽度。

高价值回归测试不是追求用例数量,而是把最可能导致崩溃、重复数据、错误状态和审核失败的风险固定下来。先抽离纯逻辑,再测试服务状态转换,随后覆盖页面交互,最后用发布候选包完成安装---启动---核心流程---卸载烟测。

在这些测试真正执行并留下证据之前,工程只能说"已有测试框架和回归设计",不能说"已通过完整自动化测试"。这种边界意识本身,就是可靠发布流程的一部分。

本文部分内容由 AI 辅助整理,测试现状、风险点与用例均来自句匠真实源码;未虚构测试通过率、设备结果、审核结论或线上数据。

相关推荐
●VON1 小时前
Flutter 鸿蒙插件适配实战:给 screen_security 补上截图与录屏防护
flutter·华为·harmonyos
贾伟康7 小时前
【口算王|20】HarmonyOS ArkTS AppGallery 发布复查实战:核对包名、版本、设备、素材和离线声明
harmonyos·arkts·appgallery·应用发布·发布审核
ChinaDragon11 小时前
HarmonyOS:Web使用Dsbridge与JavaScript完成交互
harmonyos
思录Echo14 小时前
自助易AI数字人外挂技术剖析|如何解决政企智能客服的多端适配难题?
人工智能·华为·harmonyos
方白羽15 小时前
为什么 Android 非要用 Intent 传值?
android·ios·harmonyos
不爱吃糖的程序媛21 小时前
从 0 到 1:react-native-transformer-text-input 鸿蒙化适配实录
react native·transformer·harmonyos
不爱吃糖的程序媛21 小时前
React Native 三方库鸿蒙适配实战:react-native-emoji-popup(Fabric 自定义组件)从 0 到 1
react native·harmonyos·fabric
贾伟康21 小时前
【口算王|19】HarmonyOS ArkTS 回归测试实战:覆盖启动、空数据、异常输入和重复点击
软件测试·harmonyos·arkts·回归测试·hypium
贾伟康21 小时前
【句匠|09】HarmonyOS ArkTS 学习统计实战:汇总正确率、连续学习和薄弱类型
harmonyos·arkts·appstorage·学习统计·本地统计