鸿蒙测试入门:单元测试与 Profiler

《鸿蒙测试入门:单元测试与 Profiler》

要点

鸿蒙的官方测试框架叫 Hypium,DevEco Studio 5.0 开箱即带:单元测试走 ArkTS 的 describe/it/expect,用例跑在本地模拟器或真机上。这篇做三件事——写第一条单元测试、在本地模拟器跑绿、用 Profiler 看清性能面,末尾给一份验收清单。

本文不做:ArkUI 的 UI 测试框架细节(arkuitest 一句带过)、云测试平台与上架自动化、性能调优的完整方法论——Profiler 只讲”看得见什么、怎么定性”。工程与业务代码的来历在鸿蒙实战篇,本文的测试直接往那个工程上叠加。

写第一条用例

工程向导默认生成两个测试目录:src/test 是本地单元测试,src/ohosTest 是跑在设备或模拟器上的测试(含 UI 测试)。纯逻辑先写 src/test。Hypium 的样子:

import { describe, it, expect } from '@ohos/hypium';
import { calculateTotal } from '../main/ets/util/Price';

export default function priceTest() {
  describe('priceTest', () => {
    it('单价乘数量', 0, () => {
      let total = calculateTotal(99, 3);
      expect(total).assertEqual(297);
    });
    it('非法输入抛错', 0, () => {
      let err = false;
      try {
        calculateTotal(-1, 1);
      } catch (e) {
        err = true;
      }
      expect(err).assertTrue();
    });
  });
}

几个 ArkTS 特有的地方值得点名:it 的第二个参数是超时毫秒数,必写,0 表示不限时(真有异步逻辑时写具体数字,比如 3000);断言不是 expect(x).toBe(y),而是 assertEqual / assertLarger / assertTrue 这一族,从 Jest 迁过来要换手指记忆;文件导出的函数要在 List.test.ets 里注册才会被发现——注册这一步是 Hypium 和主流框架差异最大的地方,漏了注册的测试文件就是不存在,没有任何报错提示你。

预期输出:DevEco 右侧测试面板里该用例变绿,日志出现 pass 数。环境要先”撞”一次——故意把 assertEqual(297) 改成 296 跑一遍看红、看失败断言的报错长什么样,再改回来。见过一次红色的样子,后面排查心里有底。

边界值先补齐

以 calculateTotal 为例,第一条正常用例之外,值得占位的三个边界:

  • 数量为 0:断言返回 0 而不是抛错——业务上”买 0 件”是合法输入;
  • 单价为负:断言抛错——价格模块不该静默接受负数;
  • 极大数量:断言结果不溢出(ArkTS number 口径)或按业务定义封顶。

三条写完,这个工具函数一共 5 条用例,覆盖了它全部的公开行为。多数工具类的天花板就在 10 条以内,写到”每个分支有一条用例”就该停手,别为凑数制造重复用例——重复用例在重构时是纯负担。

异步用例怎么写

it 的回调可以是 async,里面正常 await,框架会等 Promise 落定;超时参数这时候才真正起作用——异步链路卡死时用例按这个时间失败,而不是挂住整个 run:

it('异步计算完成', 3000, async () => {
  let result = await computeAsync(21);
  expect(result).assertEqual(42);
});

夹具方面,describe 里可以配 beforeEach / afterEach,每个用例前重置共享状态。我的习惯是干脆不共享:每个用例自建数据,Hypium 的用例执行顺序按注册顺序来,共享可变状态会让调换注册顺序后结果改变——这本身就是一种坏味道。

从 Jest 迁过来的对照

从 Web 前端转来做鸿蒙的人,骨架不用重学,对应关系五条:

  • describe / it 结构一致,只是 it 多一个必写的超时参数;
  • expect(x).toBe(y) → expect(x).assertEqual(y);
  • expect(x).toBeTruthy() → expect(x).assertTrue();
  • beforeEach / afterEach 同名可用;
  • jest.mock 没有等价物——ArkTS 没有运行时模块替换,替身要靠构造参数注入。

最后一条影响最大:没有模块级 mock,意味着”顺手 mock 掉一个 import”的写法不存在,依赖必须显式传参。举个最小的注入例子:页面需要”读用户设置”,把读取动作做成构造参数传进状态管理类——本地单测传一个返回固定值的替身,模拟器上传真实现,同一段业务代码两种环境各跑各的。这在工程上是好事,测试逼着代码把依赖关系摆到明面上。

本地模拟器跑测

路径一,IDE:测试用例左边的 gutter 绿箭头点下去,DevEco 会自动启动已配置的本地模拟器、安装测试包、跑完把结果回显到 Test Log 面板。第一次跑要等模拟器冷启动,后面就快了。本地模拟器跑测试不需要签名证书,这点比真机省事。

路径二,命令行。工程根目录:

hvigorw test           # 本地测试
hvigorw onDeviceTest   # 跑在设备/模拟器上,API 12 起支持,也用于覆盖率

前提一样:Device Manager 里的本地模拟器已创建并能启动。验收标准:模拟器上用例绿,且 Test Log 能翻到每条 it 的通过记录,两条都要满足。

哪些逻辑适合放 src/test:价格计算、日期格式化、状态机、数据映射这类纯 ArkTS。凡是 import 了设备能力的(网络、存储、UI 上下文),本地单测跑不起来——挪到 ohosTest 上模拟器跑,或者把设备依赖做成参数、测试里传替身。上不了的设备能力先别硬测,UI 交互交给预览器手测,这是当前工具链下最诚实的分工:预览器保外观,单测保逻辑,Profiler 保性能,三件工具各管一段,谁也不替代谁。

Profiler:性能看得见

操作路径:连上模拟器或真机,底部工具栏点开 Profiler,选会话类型(Time / CPU / Memory / FrameRate 都可单独起一个会话),点录制后回到应用里正常操作,操作完停止,等分析图渲染出来。第一次用别贪多,一次只开一个会话,图能对齐时间轴就够用。

常看三张图:

  • CPU:各线程占用曲线,先看主线程有没有长块被卡住;
  • Memory:ArkTS 堆与原生内存曲线,看有没有只涨不跌的段;
  • FrameRate / Time:帧耗时分位,找掉帧发生的时间点。

定性用法三条:启动慢,进 Time 视图看 ability 首帧前的耗时块,找最大的那块下手;滑动卡,FrameRate 视图里找超时长帧,回 Time 视图对齐时间点,看主线程那段在干什么;疑似泄漏,反复进出同一页面十次,Memory 曲线阶梯只升不降就是嫌疑对象,用堆快照对比对象数确认。

口径提醒:模拟器的性能不代表真机。定性结论(哪个函数占大头、内存是否泄漏)可用;绝对毫秒数别当真,任何写进文档的数字都要真机复测。更深的 HiTrace、HiAppEvent 打点不在这篇范围。

一次完整排查的动线

把三张图串起来用。假设现象是”列表滑动发涩”:先录 FrameRate,找到掉帧集中的时间点;切到 Time 视图对齐同一时段,看主线程那几帧在执行什么——答案往往就在那几十毫秒的调用栈里;如果栈里是列表项构建时的一次同步 JSON 解析,把解析挪到数据加载阶段或后台线程,再录一遍,对比曲线走势是否平滑。整个过程产出的是”哪段代码在占主线程”的定性结论,具体优化手段(缓存、懒加载、移出主线程)不属本文范围,但定位到”哪一段”已经完成了排查的大半。

常见坑

  • 测试文件没被跑:忘了在 List.test.ets 里注册导出的函数。现象是面板里压根不出现这个 describe,先查注册再怀疑别的。
  • it 忘写超时参数:直接编译不过,这是 Hypium 的硬约束,别嫌它烦。
  • 本地测了设备能力:import 了 @ohos 网络或存储模块,本地单测直接失败。按”纯逻辑进 src/test、设备相关进 ohosTest”分流。
  • 断言用了 Jest 语法:toBe、toEqual 都不存在,编译期就会报,改 assertEqual 族。
  • 把模拟器帧率当真机结论:模拟器渲染走的是宿主机,帧率数字系统性偏高,只能定性不能定量。

验收清单

  • src/test 至少 3 条用例:一个正常路径、一个异常路径、一个边界值(0、负数、空字符串任选),IDE 面板全绿。
  • 至少一条异步用例带明确超时参数,跑绿。
  • hvigorw onDeviceTest 在本地模拟器跑完,报告里可见全部用例。
  • 用例之间无共享可变状态:把 List.test.ets 里的注册顺序调换,结果不变。
  • Profiler 完成”启动—滑动—反复进出页面”三段录制,每段能指出最大的一块耗时在哪。
  • 测试目录 grep 不到 Thread.sleep 和裸的定时器等待。

学习顺序上,测试是”实战之后”的第一站:先把实战篇的应用跑起来,再拿它的工具类补单测;整个鸿蒙系列怎么排布、测试之后该进阶什么,看鸿蒙学习路线的对应阶段;写用例时被 ArkTS 语法卡住,回ArkTS 语法要点补对应条目。

本文数据核对时间:2025-02-24:HarmonyOS NEXT 5.0(2024-10-22 商用发布,开发者套件 5.0.0 Release 于 2024-11 推送)+ API 12 + DevEco Studio 5.0.0;@ohos/hypium 为官方单元测试框架,hvigorw 命令口径以华为开发者文档为准。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇