ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

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

【万能转换器|19】HarmonyOS ArkTS 回归测试实战:覆盖启动、空数据、异常输入和重复点击 【万能转换器19】HarmonyOS ArkTS 回归测试实战覆盖启动、空数据、异常输入和重复点击本轮证据边界当前源码共有 65 个it()声明其中 UnitConverter 14 个、CalcEngine 42 个、History 8 个、Ability 1 个。本轮只进行了静态源码复核没有执行测试套件、构建、安装或真机回归因此“65 个源码用例”绝不等于“65 个测试已通过”。启动、空数据、异常输入、重复点击和重启恢复属于基于现有缺口提出的设备回归矩阵除文中明确引用的现有测试外不把建议测试描述为当前工程已经实现。工具类应用的回归测试有一个常见误区公式算对了就认为页面稳定。实际发布后更容易暴露的问题往往发生在公式之外服务还没初始化时页面已经可点、空列表进入编辑模式、非法输入留下上一次结果、连续点击“转换”写入多条历史、异步删除过程中再次操作、默认单位刚修改就返回、剪贴板写入失败却没有反馈。这些状态很难靠一条“1 米等于 100 厘米”覆盖。“万能转换器”的真实源码已经具备一层不错的算法测试。entry/src/ohosTest/ets/test中的List.test.ets聚合了Ability.test.ets、UnitConverter.test.ets、CalcEngine.test.ets和History.test.ets按源码中的it()声明统计共有 65 个用例14 个单位换算用例、42 个计算引擎用例、8 个历史模型用例和 1 个模板式 Ability 断言。它们覆盖温度非线性换算、NaN/Infinity 格式化、零利率、无效 BMI、90 度正切、错误进制、序列化容错等边界。但“有 65 个测试文件中的用例”不等于“本次已经执行并全部通过”本文没有伪造测试运行结果。源码证据还显示现有 ohosTest 主要保护纯函数和模型Ability.test.ets只是模板断言没有真实启动、路由和页面操作未发现针对ConvertPage、HistoryPage、FavoriteToolsPage、DefaultUnitsPage的 UI 自动化脚本。尤其是重复点击、异步服务未就绪、删除中途失败和重启后的持久化恢复仍需要设备级回归。本文面向 HarmonyOS 5.0 及以上版本基于上述测试源码和五个真实页面建立“纯函数单测 服务状态测试 ArkUI 页面回归 安装启动稳定性”的分层方案。唯一标记AGC18-HMOS-18-19-REGRESSION-STARTUP-EMPTY-INVALID-REPEAT一、先把“已有覆盖”和“计划覆盖”分开测试文章最忌讳把测试计划写成测试结果。当前源码能确认的事实如下层级已有源码可确认覆盖仍缺少的证据单位换算UnitConverter.test.ets14 个声明用例本次执行报告计算引擎CalcEngine.test.ets40 个声明用例本次执行报告历史模型History.test.ets8 个声明用例Preferences 真机读写AbilityAbility.test.ets1 个模板断言真实安装与启动ArkUI 页面未发现对应 UI 脚本无路由、空态、点击、弹窗、返回本文后续出现的 UI 场景、测试代码和门禁值若没有在当前工程实现都会明确标为“建议方案”。这能避免把未来工作误写成现状也便于团队逐条落地。二、现有63个用例保护了什么List.test.ets的聚合关系很直接export default function testsuite() { abilityTest(); unitConverterTest(); calcEngineTest(); historyModelTest(); }其中真正有业务价值的是三组纯逻辑测试单位换算长度、重量、温度、数据容量、时间、速度、批量换算与格式化。计算引擎房贷、BMI、体脂、个税、利息、欧姆定律、合阻、几何、日期、汇率、进制和金额大写。历史模型ID、序列化往返、空值、畸形 JSON、缺失字段补默认值和时间格式化。这些用例执行快、定位准适合作为每次提交的第一道门禁。它们解决“计算核是否被改坏”却不能回答页面是否能正常启动和交互。三、现有用例中的边界设计值得保留测试没有只写正常样例而是主动覆盖异常值it(convertAll with NaN propagates NaN, 0, () { const results convertAll(group, m, NaN); for (const result of results) { expect(Number.isNaN(result.value)).assertTrue(); expect(result.display).assertEqual(-); } });CalcEngine.test.ets还检查BMI 输入 0 → 无效等级 90° 正切 → NaN 错误进制字符串 → validfalse 无效长度估算 → 空数组 零利率房贷 → 本金按期数均分 并联电阻 → 忽略 0 和负值这类用例建立了稳定的领域合同。页面回归不必再次证明所有数学公式只要验证 UI 能把非法输入映射到这些既定结果并且不保存错误历史。四、启动回归的风险在异步初始化EntryAbility.onCreate()会启动三个异步服务HistoryService.init(this.context).catch(...); SettingsService.init(this.context).catch(...); FavoriteToolsService.init(this.context).catch(...);这些 Promise 没有在loadContent(pages/Index)前统一等待。因此页面有机会先显示用户也可能在服务尚未完成初始化时点击收藏、转换或设置。源码做了一些局部保护例如收藏页用try/catch捕获服务未就绪转换页保存历史时也会静默跳过但这并不是一个统一的启动状态。启动回归至少应验证冷启动、热启动、后台恢复、清数据后首次启动和快速点击首屏工具。尤其要确认“页面看见了但服务不可用”时用户不会得到成功假象。五、冷启动测试要观察四个时点建议把冷启动拆成四个可观测时点T0进程创建 T1WindowStage 创建 T2Index 首帧可见 T3History / Settings / Favorites 全部 ready当前源码没有公开统一的AppReady状态。建议方案是由启动编排器等待必要服务或者把准备状态写入 AppStorage// 建议方案非当前源码 export type StartupState booting | ready | degraded; await Promise.all([ HistoryService.init(context), SettingsService.init(context), FavoriteToolsService.init(context), ]); AppStorage.setOrCreateStartupState(startupState, ready);页面测试就能明确断言booting时关键写操作不可用ready后才响应某个非关键服务失败时进入degraded而不是无限加载。六、Ability.test 目前不能证明真实启动当前Ability.test.ets的唯一用例是it(assertContain, 0, () { let a abc; let b b; expect(a).assertContain(b); expect(a).assertEqual(a); });它证明 Hypium 套件可以加载一个简单断言却没有启动EntryAbility、等待首页、点击 Tab 或检查崩溃。它不应被计入“启动流程已覆盖”的证据。这并不意味着要删除模板用例。更合理的做法是保留一个环境冒烟用例同时新增真实启动测试确认应用主页面出现、底部导航可见、系统返回可用日志中没有未捕获异常。七、空数据回归要覆盖三类页面项目中至少有三种空数据语义HistoryPage暂无记录提示“使用任意工具后会自动记录”。HistoryPage收藏子 Tab还没有收藏提示点击星标。FavoriteToolsPage还没有收藏的工具。空态测试不能只截图文字还应验证操作约束空历史时“清空”是否隐藏或无害 空筛选结果时编辑模式是否能退出 空收藏时“全部清空”是否不可触发 从空态新增第一条数据后是否立即切换为列表 删除最后一条后是否回到正确空态 重启后空态是否稳定HistoryPage.allSelected()已经在过滤结果为空时返回false这是一个值得直接单测的边界。八、历史页的空态文案来自状态函数真实代码把空态标题和提示集中到两个方法private emptyTitle(): string { if (this.activeTab favorite) return 还没有收藏; return 暂无记录; } private emptyHint(): string { if (this.activeTab favorite) { return 点击列表项右侧的 ☆ 即可收藏; } return 使用任意工具后会自动记录; }这比在多个 Builder 分支中重复字符串更容易测试。建议进一步把“过滤类型 → 空态模型”提取成纯函数让单测直接覆盖全部、转换、计算、收藏四个 Tab。UI 自动化仍需验证文字真正显示因为纯函数正确不代表布局没有被底栏遮住也不代表切换 Tab 后页面用的是最新状态。九、异常输入要同时检查按钮、结果和历史ConvertPage对输入的处理有三条链路private canConvert(): boolean { const value parseFloat(this.inputText); return Number.isFinite(value); }this.results convertAll( this.group, this.fromKey, Number.isFinite(value) ? value : NaN, );if (trimmed.length 0) return; const value parseFloat(trimmed); if (!Number.isFinite(value)) return;因此异常输入回归不能只看“转换按钮变灰”。还要确认结果显示为-、复制与清空按钮状态合理、离开页面不会写历史、再次输入合法值能恢复。十、parseFloat 带来一个需要测试的语义parseFloat()会接受数字前缀。例如输入12abc时可能得到12Number.isFinite(12)为真。当前canConvert()因此可能把包含尾随字符的字符串视为可转换。这不是本文臆测的崩溃而是 JavaScript/ArkTS 数值解析语义与当前实现组合出的测试点。产品需要明确若允许宽松输入应在 UI 中把标准化后的数值反馈给用户。若只允许完整数字应使用更严格的正则或数值解析函数并为12abc、多个小数点、单独负号、指数形式和空格建立用例。建议用例// 建议方案非当前源码 expect(parseStrictNumber(12)).assertEqual(12); expect(parseStrictNumber(12.5)).assertEqual(12.5); expect(parseStrictNumber(12abc)).assertUndefined(); expect(parseStrictNumber(-)).assertUndefined();十一、非法输入不能留下旧结果当合法值改为非法值时recomputeResults()会把 NaN 传给convertAll()现有单测证明结果格式会变为-。回归测试还应覆盖状态切换顺序输入 1 → 出现有效结果 清空 → 所有结果变为 - 输入 abc → 按钮禁用结果仍为 - 输入 2 → 结果恢复 切换源单位 → 结果按 2 重新计算 点击结果行作为新输入 → 输入和结果同步这类“有效 → 无效 → 有效”比单独输入一个非法值更容易发现缓存、Watch和列表刷新问题。十二、重复点击转换会触发多次异步保存转换按钮的点击处理为private handleConvertClick(): void { this.recomputeResults(); this.recordHistoryIfValid(true); }按钮只根据输入是否合法启用没有saving或busy状态。用户连续点击会连续调用HistoryService.add()。服务层存在“相同 kind、type、summary 的记录在 5 秒内更新原记录”的去重逻辑这是保护层但页面仍可能弹出多个完成提示异步返回顺序也值得验证。重复点击测试应该快速点击 2、5、10 次然后断言页面不冻结 历史数量符合去重合同 最终记录时间正确 完成提示不会堆叠到不可用 按钮状态能恢复 离开页面不会再额外重复保存十三、去重合同应在服务层直接单测HistoryService.add()的关键逻辑是若最近记录的kind、type、summary相同且时间差小于 5 秒则更新已有记录并移到顶部否则创建新记录。这段逻辑比页面点击更适合服务测试。建议注入可控时钟// 建议方案非当前源码 interface Clock { now(): number; } const clock new FakeClock(1_000); await service.add(payload); clock.advance(4_000); await service.add(payload); expect(service.listRecent(10).length).assertEqual(1); clock.advance(6_000); await service.add(payload); expect(service.listRecent(10).length).assertEqual(2);如果不注入时钟测试只能真实等待 5 秒运行慢且容易抖动。十四、删除选中存在串行异步窗口历史批量删除当前逐条等待for (const id of ids) { await service.remove(id); } this.selected []; this.editMode false;remove()每次都会过滤缓存、全量写回 Preferences 并广播。选中 50 条记录就会发生 50 次序列化、写入和刷新。在删除期间页面没有显式deleting状态用户可能再次点击。回归测试应覆盖连续点击“删除”是否重复执行。删除过程中返回页面是否留下半完成状态。中间一次写入失败时前面已删和后面未删如何展示。删除最后一条后空态是否正确。编辑模式和selected是否总能复位。更好的服务接口是一次removeMany(ids)单次持久化并返回删除数量。十五、清空和恢复默认都需要幂等测试HistoryPage.confirmClearAll()和DefaultUnitsPage.resetAll()都有确认框成功后显示 Toast。幂等回归应验证取消 → 数据完全不变 确认一次 → 数据恢复目标状态 确认后再次触发 → 不崩溃、不产生错误状态 操作中快速返回 → 重进页面状态正确 写入失败 → 不显示成功 Toast 重启 → 清空/默认状态保持当前代码的成功 Toast 都放在 Promisethen()中这一点是正确方向失败时只记录日志用户不可见仍需补测试和交互。十六、收藏页要测废弃key和快速切换FavoriteToolsPage.itemsOf()会解析收藏 JSON并通过findToolByKey()过滤已经不存在的工具 keyset.forEach((key: string) { const item findToolByKey(key); if (item) output.push(item); });这是迁移兼容的重要边界。建议单测以下输入空字符串 畸形 JSON 重复 key 不存在 key 一个合法 一个废弃 key 大量 keyUI 回归则要快速点击同一星标检查最终收藏状态与 Preferences 一致。当前toggle()是异步写入页面没有针对同一 key 的请求锁连续点击可能形成顺序竞争不能只凭最后一次动画判断持久化结果。十七、默认单位要测设置、回退和路由ConvertPage.aboutToAppear()会读取defaultUnitOf()并检查该 key 是否仍存在const preferred defaultUnitOf(type, this.defaultUnitsJson); const exists this.group.units.find( (unit: Unit) unit.key preferred, ); this.fromKey exists ? preferred : this.group.baseKey;这条回退链路应覆盖从未设置时使用内置默认单位。修改后进入转换页源单位立即变化。Preferences 中是废弃 key 时回退baseKey。恢复默认后各类型回到预期单位。快速切换多个类型不会把 key 写到错误类型。设置失败时页面不能假装已保存。当前页面的selectUnit()失败只写日志UI 可能先因 AppStorage 更新或列表状态出现短暂错觉需要真机验证实际时序。十八、回归矩阵应按状态而不是页面数量展开一个可执行的最小矩阵如下场景初始状态操作断言冷启动清数据启动并立刻点工具不崩溃核心页可用空历史无记录进入历史正确空态无无效编辑空收藏无收藏进入收藏正确空态非法输入12abc点击转换按产品合同拒绝或标准化状态恢复合法→非法→合法连续编辑结果不残留重复转换合法输入快点10次历史与提示符合去重合同重复收藏未收藏快点10次最终 UI 与持久化一致批量删除多条选中连续确认单次执行状态复位恢复默认自定义单位确认后重启默认单位持续生效后台恢复页面有草稿输入切后台再返回不闪退状态符合设计矩阵中的每一行都要记录设备、系统版本、包版本、前置数据、操作步骤和结果避免“我点过了没问题”的口头结论。十九、把测试放进发布门禁而不是最后一天建议流水线分四层提交门禁 纯函数单测 静态检查 合并门禁 服务层持久化测试 关键页面 UI 自动化 候选包门禁 assembleApp / 签名包 安装启动核心流程卸载 上架门禁 AppAnalyzer 上架前体检 DevEco Testing 或 AGC 云测试华为开发者测试服务把质量覆盖划分为单元测试、UI 测试、专项测试、上架预检和线上监测。对于这个项目最划算的新增自动化不是再写几十个同类公式样例而是补上启动、空态、非法输入、重复点击、持久化恢复和删除失败。还要强调本文只审阅了测试源码没有在此发布流程中执行项目 ohosTest、安装包或真机测试因此不能写“65 个用例全部通过”“真机零崩溃”或“审核已通过”。二十、最小落地顺序与结论基于当前代码建议按风险和收益排序保留现有 62 个业务逻辑用例和 1 个环境模板用例并在 CI 真实执行、保存报告。把 Ability 模板用例升级为真实启动冒烟。为 HistoryService 的 5 秒去重注入时钟并直接单测。为收藏、默认单位和 Preferences 恢复补服务测试。建立ConvertPage的合法、非法、恢复和重复点击 UI 回归。建立历史空态、最后一条删除、批量删除和失败恢复回归。给异步按钮增加saving/deleting/toggling状态再验证快速点击。对候选包执行安装、冷启动、核心流程、后台恢复和卸载。使用 AppAnalyzer、DevEco Testing 或 AGC 云测试补充稳定性、兼容性和上架预检。“万能转换器”的算法层并不是测试荒地。温度换算、NaN、零利率、错误 JSON、进制非法输入等边界已经被写进源码用例。真正的缺口位于逻辑层之上服务异步初始化、ArkUI 状态变化、Preferences 持久化、快速重复操作和候选包生命周期。回归测试的目标也不是让用例数量漂亮而是让每次修改后都能回答四个问题能否启动空数据能否自洽异常输入能否安全收敛用户连续操作能否得到唯一且可解释的结果。把这四条做成自动化门禁才是工具类 HarmonyOS 应用稳定上架和持续迭代的底座。官方资料可继续参考华为开发者的 HarmonyOS 开发者测试服务入门、DevEco Testing 资源与工具、开发态快速定位 AppFreeze 冻屏、提交 HarmonyOS 应用 与 应用市场审核政策。具体命令、设备能力和测试框架 API应以项目目标 SDK、DevEco Studio 与 DevEco Testing 对应版本为准。AI 辅助声明本文在人工核对真实 HarmonyOS 测试源码、Ability 生命周期、服务初始化与输入校验后使用 AI 辅助整理结构、润色表达并生成配图测试数量、覆盖范围和未执行边界均以文中列出的源码证据为准。CSDN-SERIES:ALL-163250228
返回列表