news 2026/9/18 4:11:46

鸿蒙预览场景模拟:@ohos/hamock 模拟框架详解与源码级实践指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
鸿蒙预览场景模拟:@ohos/hamock 模拟框架详解与源码级实践指南

鸿蒙预览场景模拟:@ohos/hamock 模拟框架详解与源码级实践指南

【免费下载链接】cann-recipes-harmony-infer本项目为鸿蒙开发者提供基于CANN平台的业务实践案例,方便开发者参考实现端云能力迁移及端侧推理部署。项目地址: https://gitcode.com/cann/cann-recipes-harmony-infer

Hamock 是 OpenHarmony 生态中的轻量模拟框架,专为 DevEco Studio 预览场景设计,允许开发者在组件预览时通过@MockSetup装饰器重定义组件方法或重赋值组件属性,从而让 UI 预览摆脱对真实业务数据与接口的依赖。本文以当前仓库(cann-recipes-harmony-infer)中 Soble 案例所依赖的 @ohos/hamock 源码包 为蓝本,结合框架底层实现,系统讲解 Hamock 的安装、API 使用、执行原理与约束限制,帮助你在鸿蒙应用开发中快速搭建可独立运行的预览 Mock 环境。

一、Hamock 是什么:预览场景的模拟框架

Hamock 是 OpenHarmony 上针对预览场景(DevEco Studio Previewer)提供的模拟框架。它的核心能力是:让开发者在不修改业务代码的前提下,为 UI 组件指定一组“预览专用的替身数据”——在预览器打开组件时,用 Mock 数据替换掉原本依赖网络请求、原生能力或复杂状态初始化的方法与属性,使页面预览始终可运行、可展示。

它解决的问题非常具体:ArkTS 声明式范式组件在预览时,aboutToAppear、构造参数、@Prop等属性的初始化链路往往依赖外部环境(如模型加载、接口回调),一旦依赖缺失,预览器便无法渲染出完整页面。Hamock 通过@MockSetup装饰器提供了一条“预览专用初始化通道”,在不触碰业务逻辑的前提下注入 Mock 行为。

从当前仓库的依赖配置可以确认 Hamock 的实际接入方式:在 Soble 工程的 oh-package.json5 中,@ohos/hamock1.0.0版本被声明在devDependencies中(与@ohos/hypium并列),在 oh-package-lock.json5 中锁定了版本与校验信息,说明它作为开发期依赖随工程一并分发。

二、下载安装

Hamock 通过 ohpm(OpenHarmony 包管理器)安装,命令如下:

ohpm install @ohos/hamock

安装完成后,工程中会生成对应的锁文件与依赖目录。在本仓库中,安装产物即位于 harmony_infer/harmony_os_next/Soble/oh_modules/@ohos/hamock/ 目录下,其 oh-package.json5 声明了该包的元信息:

  • name:@ohos/hamock
  • version:1.0.0
  • description:A mock framework for OpenHarmony application.
  • main:index.ets(入口模块,对外导出MockSetupMockKitwhenArgumentMatchers
  • types:index.d.ts(TypeScript 类型声明,供 IDE 智能提示与静态检查使用)
  • license:Apache-2.0

从 index.ets 可以看到包的导出面:

export { MockSetup, MockKit, when } from './src/main/mock/MockKit'; export { ArgumentMatchers } from './src/main/mock/ArgumentMatchers';

也就是说,安装后你在业务代码中实际可用的是四个核心导出:MockSetup(装饰器)、MockKit(模拟器)、when(存根配置入口)与ArgumentMatchers(参数匹配器)。

三、核心概念:@MockSetup 装饰器

@MockSetup用于修饰 Mock 方法,仅支持声明式范式的组件。当开发者预览该组件时,预览运行时将在组件初始化时执行被@MockSetup修饰的方法。开发者可以在这个被修饰的方法内重定义组件的方法,或重赋值组件的属性,这些变更只在预览时生效,不影响真机运行。

说明:@MockSetup修饰的方法仅在预览场景会自动触发,并先于组件的aboutToAppear执行

在仓库附带的 MockKit.ts 源码 中可以看到MockSetup的底层实现,它本质上是对组件aboutToAppear生命周期的“前置包装”:

function MockSetup(target: Object, propertyName: string | Symbol, descriptor: TypedPropertyDescriptor<() => void>): void { const aboutToAppearOrigin = target.aboutToAppear; const setup = descriptor.value; target.aboutToAppear = function (...args: any[]) { if (target.__Param) { // copy attributes and params of the original context // 将组件上下文中的属性与参数复制到当前执行上下文 } if (setup) { // apply the mock content setup.apply(this); // 先执行 @MockSetup 修饰的方法 } if (aboutToAppearOrigin) { // append to aboutToAppear function of the original context aboutToAppearOrigin.apply(this, args); // 再执行原始 aboutToAppear } } }

这段实现揭示了三个关键设计:

  1. 执行顺序@MockSetup方法先于组件原有aboutToAppear执行,因此 Mock 注入的数据可以在业务初始化逻辑读取之前就位;
  2. 上下文一致性:通过target.__Param将组件上下文(含@Prop等参数)复制到装饰器方法执行上下文,保证 Mock 方法内访问this.xxx语义正确;
  3. 零侵入:原始aboutToAppear被保存并追加执行,业务生命周期逻辑不被破坏。

四、使用示例一:Mock UI 组件的方法

在 ArkTS 页面代码中引入 Hamock,在目标组件中定义一个方法并用@MockSetup修饰,在该方法内使用MockKit模拟目标方法:

import { MockKit, when, MockSetup } from '@ohos/hamock'; @Entry @Component struct Index { ... @MockSetup randomName() { let mocker: MockKit = new MockKit(); let mockfunc: Object = mocker.mockFunc(this, this.method1); // mock 指定的方法在指定入参的返回值 when(mockfunc)('test').afterReturn(1); } ... // 业务场景调用方法 const result: number = this.method1('test'); // in previewer, result = 1 }

上例中,this.method1('test')在真机上执行真实逻辑,而在预览器中会命中 Mock 存根并返回1

从 MockKit.ts 的源码看,mockFunc的替换机制是:通过findName找到目标方法在对象上的属性名,然后用包装函数f覆盖originalObject[name],同时把原方法保存在recordMockedMethod映射中以便恢复。包装函数在执行时调用getReturnInfo查询存根表stubs

  • 有匹配的存根(action):执行存根动作并返回结果;
  • 无匹配存根:返回undefined

每次被调用时,recordMethodCall都会以“方法名(参数列表)”为键记录调用次数,为后续的verify断言提供数据。

when的语义在 ExtendInterface.ts 中实现:when(mockfunc)('test')本质是先通过stub()暂存参数(这里是入参'test'),随后.afterReturn(1)调用stubMockedCall把“入参 → 返回动作”的映射写入 MockKit 的stubs表。框架提供的全部存根行为包括:

方法行为典型用途
afterReturn(value)固定返回指定值替代返回固定数据的接口/方法
afterReturnNothing()返回undefined模拟无返回值的调用
afterAction(action)执行自定义动作函数模拟带副作用的调用
afterThrow(msg)抛出指定异常信息模拟异常分支

五、使用示例二:Mock UI 组件的属性

在 ArkTS 页面代码中引入 Hamock,在目标组件中定义一个方法并用@MockSetup修饰,在该方法内对需要 Mock 的属性重新赋值:

import { MockSetup } from '@ohos/hamock'; @Component struct Person { @Prop species: string; ... // 在 @MockSetup 片段中,定义对象属性 @MockSetup randomName() { this.species = 'primates'; } ... // 业务场景调用属性(如果从初始化到调用期间,该属性无变化) const result: string = this.species; // in previewer, result = primates }

上例中,@Prop species在预览时会被赋值为'primates',从而让依赖该属性的 UI 分支能够正常渲染。属性的 Mock 不经过MockKit存根表,而是依赖@MockSetup方法先于aboutToAppear执行的时序:赋值动作发生在业务代码读取属性之前。

六、深入原理:MockKit 的完整 API 与验证机制

除了@MockSetupwhen/afterReturn组合,MockKit 还提供了一组面向对象级 Mock 与调用验证的 API,类型声明见 index.d.ts:

6.1 MockKit 核心方法

方法说明
mockFunc(obj, func)将对象上的指定方法替换为 Mock 函数,返回包装函数
mockObject(obj)浅拷贝对象并将其上所有函数类型成员逐一 Mock,返回 Mock 后的对象副本
verify(methodName, argsArray)校验某方法以指定参数被调用的次数,返回VerificationMode
ignoreMock(obj, func)还原指定对象的指定方法(忽略 Mock)
clear(obj)还原指定对象上所有被 Mock 过的方法
clearAll()清空当前 MockKit 实例的全部存根与调用记录

其中verify配合 VerificationMode.js 提供次数断言:

export interface VerificationMode { times(count: Number): void // 断言恰好调用 count 次 never(): void // 断言从未调用 once(): void // 断言恰好调用 1 次 atLeast(count: Number): void // 断言至少调用 count 次 atMost(count: Number): void // 断言至多调用 count 次 }

6.2 ArgumentMatchers 参数匹配

when(mockfunc)的入参除了字面量,还可以使用参数匹配器,实现“任意值/按类型/按正则”的存根匹配。匹配器定义在 ArgumentMatchers.ts 中:

export class ArgumentMatchers { static any; // 匹配任意入参 static anyString; // 匹配任意字符串 static anyBoolean; // 匹配任意布尔值 static anyNumber; // 匹配任意数字 static anyObj; // 匹配任意对象 static anyFunction; // 匹配任意函数 static matchRegexs(Regex: RegExp): void // 按正则匹配字符串入参 }

典型用法:

// 只要入参是任意字符串,就返回 1 when(mockfunc)(ArgumentMatchers.anyString).afterReturn(1); // 入参匹配正则 /^test/ 时返回 2 when(mockfunc)(ArgumentMatchers.matchRegexs(/^test/)).afterReturn(2);

从源码实现看,stubApply在写入存根时会把匹配器映射为内部占位键(如"<any String>"),而getReturnInfo在查询时通过matcheReturnKey做类型推导与正则匹配,从而把“匹配器定义”与“按入参查存根”两阶段衔接起来。

6.3 一次完整 Mock 的调用链

综合源码,一次方法 Mock 的完整链路为:

  1. new MockKit()初始化存根表stubs、调用记录表recordCalls、被 Mock 方法记录表recordMockedMethod
  2. mocker.mockFunc(this, this.method1)定位属性名并替换为包装函数,原方法被存档;
  3. when(mockfunc)('test')暂存参数,.afterReturn(1)将「入参 → 返回 1 的动作」写入stubs
  4. 预览器触发组件初始化,@MockSetup方法执行(先于aboutToAppear),完成上述注册;
  5. 业务代码调用this.method1('test'),命中包装函数,getReturnInfo查表得到返回 1 的动作并执行,同时记录一次调用;
  6. 如需断言,用verify('method1', ['test']).once()校验调用次数。

七、约束与限制

根据 Hamock 官方 README(即本仓库中的 hamock README),该框架在以下版本验证通过:

组件版本
DevEco Studio4.1 (4.1.3.400)
SDKAPI11 (4.1.0.36)

MockSetup仅在 API11 支持。因此:

  • 若目标工程的 SDK 版本低于 API11,无法使用@MockSetup装饰器,只能考虑在预览之外的其他测试手段;
  • 从 CHANGELOG.md 的版本记录看,1.0.0-rc首次提供 DevEco Studio 预览器场景使能的MockSetup装饰器,1.0.0修复了once断言问题,本仓库锁定的是修复后的1.0.0正式版;
  • 使用@MockSetup时,Mock 逻辑仅作用于预览场景,真机运行不受影响,这既是能力也是边界——不能把预览 Mock 当作单元测试替身或运行时数据源。

八、在仓库中的工程化落地

本仓库的 Soble 案例工程 是 Hamock 的典型落地场景:该工程以@ohos/hamock: 1.0.0作为devDependencies依赖,与@ohos/hypium一并用于开发调试(详见 oh-package.json5 与 oh-package-lock.json5)。在涉及 Sobel 图像处理、模型推理等依赖原生能力与算力环境的页面中,预览器无法真正执行端侧推理,此时便适合用 Hamock 在预览场景中注入可展示的模拟数据,保证 UI 开发与调试的独立进行。

需要提醒的是,仓库对oh_modules目录是随依赖安装自动生成的,若在你的工程中复现,请以ohpm install拉取对应版本,而不要手工拷贝本仓库的oh_modules内容。

九、参与贡献与开源协议

Hamock 遵循 Apache License 2.0 开源协议(详见其包声明中的license字段)。若在使用过程中发现缺陷,可以向 OpenHarmony 的 testfwk 相关仓库提交 Issue 或 PR 参与共建。

结语

Hamock 以“预览场景专用”为定位,通过@MockSetup装饰器 +MockKit存根机制,为 ArkTS 声明式组件的 UI 预览提供了一条低成本、零侵入的 Mock 通道。结合其源码实现,我们可以清晰地看到:方法 Mock 基于“替换属性 + 存根表查询 + 调用记录”三件套,属性 Mock 依赖“先于aboutToAppear执行”的生命周期时序,而when/ArgumentMatchers/VerificationMode则分别承担存根配置、灵活匹配与调用验证的职责。掌握这套机制,你就能在鸿蒙应用开发中让预览器“跑得起来、看得见数据”,显著提升 UI 侧开发调试效率。

【免费下载链接】cann-recipes-harmony-infer本项目为鸿蒙开发者提供基于CANN平台的业务实践案例,方便开发者参考实现端云能力迁移及端侧推理部署。项目地址: https://gitcode.com/cann/cann-recipes-harmony-infer

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 4:09:23

Agent-Reach实践:构建多智能体系统的统一触达层

1. 项目概述与核心问题1.1 从"Agent能做什么"到"Agent怎么被找到""Agent-Reach"这个名字乍一看有点抽象&#xff0c;但如果拆开理解就很直白&#xff1a;Agent代表智能体&#xff0c;Reach代表触达、覆盖、到达。合起来&#xff0c;它解决的问题…

作者头像 李华
网站建设 2026/9/18 4:09:19

当 Gemini 3.8 Live 全双工对话,TaoToken Key 如何分并发

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 4:07:29

虚拟机忘记密码?PE引导与单用户模式重置Windows/Linux密码

群里隔三差五就有人问一句&#xff1a;“虚拟机破解密码怎么做&#xff1f;”点进去一看&#xff0c;截图多半是卡在登录界面。聊到最后基本都是同一个答案&#xff1a;不是要去动别人的系统&#xff0c;而是自己那台 Windows 或 Linux 虚拟机密码忘了&#xff0c;里面还有没来…

作者头像 李华
网站建设 2026/9/18 4:07:03

老款 Mac 升级 macOS 完整指南:OpenCore Legacy Patcher 从零到开机

老款 Mac 升级 macOS 完整指南&#xff1a;OpenCore Legacy Patcher 从零到开机 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher 按流程走完&#xff0c;老款 …

作者头像 李华