HarmonyOS 应用开发《掌上英语》第61篇:HarmonyOS 单元测试——从 LocalUnit.test 到 Ability.test
HarmonyOS 单元测试——从 LocalUnit.test 到 Ability.test

一、测试在项目中的地位
在 11 模块架构的英语学习 App 中,测试是保障代码质量的关键手段。每个模块(homePage、topicPage、minePage 以及 commonLib 等)都包含了两种测试目录:src/test/ 和 src/ohosTest/。
commons/commonLib/
└── src/
├── test/
│ ├── List.test.ets # 测试列表入口
│ └── LocalUnit.test.ets # 本地单元测试
├── ohosTest/
│ ├── ets/
│ │ └── test/
│ │ ├── Ability.test.ets # Instrumentation 测试
│ │ └── List.test.ets # 测试列表入口
│ └── module.json5
这两种测试目录代表了 HarmonyOS 中两种不同的测试类型:LocalUnitTest 和 InstrumentationTest(ohosTest)。
二、test 目录 vs ohosTest 目录
src/test/ 目录(LocalUnitTest)
- 运行在本地 JVM 上,不依赖真机或模拟器
- 只能测试纯 TypeScript/ArkTS 逻辑代码
- 不能调用 @kit API(如 UI 组件、系统服务)
- 运行速度快,适合频繁执行
src/ohosTest/ 目录(InstrumentationTest)
- 运行在真机或模拟器上
- 可以测试完整的能力调用,包括 UI 组件、系统 API
- 需要打包安装到设备上运行
- 运行速度慢,通常作为 CI 流程的一部分
选择原则:纯逻辑测试(如工具函数、模型计算)放在 LocalUnitTest;涉及 UI 渲染、系统 API 的测试放在 ohosTest。
三、LocalUnitTest:纯逻辑测试实践
LocalUnitTest 适合测试 ViewModel 中的计算方法、数据模型的序列化/反序列化、工具函数的正确性。
我们的项目中,LocalUnit.test.ets 文件的内容模板:
import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
export default function localUnitTest() {
describe('localUnitTest', () => {
beforeAll(() => {
// 在所有测试用例之前执行一次
});
beforeEach(() => {
// 在每个测试用例之前执行
});
afterEach(() => {
// 在每个测试用例之后执行
});
afterAll(() => {
// 在所有测试用例之后执行一次
});
it('assertContain', 0, () => {
let a = 'abc';
let b = 'b';
expect(a).assertContain(b);
expect(a).assertEqual(a);
});
});
}
这个模板使用了 @ohos/hypium 测试框架,这是 HarmonyOS 官方提供的轻量级测试框架。它提供了类似于 Jest 的 API 风格:describe、it、expect。
在实际项目中可以扩展的测试用例:
it('WordCard constructor test', 0, () => {
const card = new WordCard(1, 'apple', '/ˈæpl/', 'n.', '苹果',
'I eat an apple.', '我吃了一个苹果。');
expect(card.word).assertEqual('apple');
expect(card.translation).assertEqual('苹果');
expect(card.category).assertEqual('basic');
});
it('PreferenceUtil put and get', 0, () => {
PreferenceUtil.getInstance('test').put('key', 'value');
const result = PreferenceUtil.getInstance('test').get('key');
expect(result).assertEqual('value');
});
需要注意的是,PreferenceUtil 依赖 ContextUtils 和系统 Preferences API,因此严格的本地单元测试无法测试它——这应该放在 ohosTest 中。
四、InstrumentationTest:UI 测试实践
InstrumentationTest 运行在真机或模拟器上,可以测试 UI 组件的创建和交互。
Ability.test.ets 的典型内容:
import { describe, it, expect } from '@ohos/hypium';
import { Driver, ON } from '@ohos.UiTest';
export default function abilityTest() {
describe('AbilityTest', () => {
it('test_page_load', 0, async () => {
// 创建 UI 测试驱动
let driver = Driver.create();
await driver.delayMs(1000);
// 查找页面上的文字元素
let text = await driver.findComponent(ON.text('英语学习'));
expect(text != null).assertTrue();
});
});
}
InstrumentationTest 的核心能力:
- UI 组件查找:通过
Driver查找页面上的组件(按文本、ID、类型) - 模拟用户操作:点击、滑动、输入
- 页面跳转验证:验证路由跳转后的页面内容
- 状态验证:验证 @State/@Local 变量的变化
五、ohosTest 中的测试组织
每个模块的 ohosTest 目录结构一致:
features/homePage/src/ohosTest/ets/test/
├── Ability.test.ets # 核心测试文件
└── List.test.ets # 测试列表,注册所有测试套件
List.test.ets 的作用是汇总所有测试套件:
import abilityTest from './Ability.test';
export function TestList() {
abilityTest();
}
这样做的好处是:新增测试文件时,只需要在 List.test.ets 中注册即可,module.json5 中配置的入口文件保持不变。
ohosTest 目录下的 module.json5 配置了测试模块的信息:
{
"module": {
"name": "homePage_ohosTest",
"type": "test",
"srcPath": "",
"description": "HomePage test module",
"testRunner": "ohosTestRunner"
}
}
六、测试策略与覆盖率
在 11 模块架构下,建议的测试策略是:
-
核心逻辑全覆盖:commonLib 中的 Manager(PreferenceUtil、NewWordManager、AudioPlayer)和 Utils(Logger、ContextUtils)应该有完整的 LocalUnitTest 覆盖
-
关键页面 UI 测试:homePage 的 MainPage、topicPage 的 WordCardPage、minePage 的 MinePage 应该有 InstrumentationTest,验证页面加载和基本交互
-
边界情况测试:数据处理函数(如数据序列化、生词本增删改查)需要测试边界情况(空数据、重复数据、大数据量)
-
构建集成:在
hvigorfile.ts中配置运行测试的任务,CI/CD 流水线中自动执行
七、总结
HarmonyOS 的测试体系提供了 LocalUnitTest 和 InstrumentationTest 两种互补的方式。LocalUnitTest(src/test/)负责纯逻辑的快速验证,InstrumentationTest(src/ohosTest/)负责真机环境下的集成测试。在我们的项目中,每个模块都遵循这种双测试目录的结构,为代码质量提供了基础保障。开发者应该根据测试目标选择合适的测试类型,并且保持测试与代码的同步演进。
更多推荐



所有评论(0)