引言
测试是保证应用质量的关键环节。鸿蒙(HarmonyOS)提供了完整的测试框架体系,从 @ohos.test.uitest 到 @ohos.unittest,覆盖了单元测试、UI 自动化测试、压力测试等各个层面。本文将从零开始,带你掌握鸿蒙应用的测试方法。
一、测试框架概览
鸿蒙测试体系主要分为三类:
| 测试类型 | 框架/工具 | 用途 | 运行环境 |
|---|---|---|---|
| 单元测试 | @ohos.unittest | 函数/类级别测试 | DevEco Studio |
| UI 自动化 | @ohos.test.uitest | UI 交互、页面跳转 | 真机/模拟器 |
| 压力测试 | SmartPerf + DevEco | 性能、内存、CPU | 真机 |
| 集成测试 | DevEco Test Runner | 多模块联调 | DevEco Studio |
二、单元测试实战
1. 测试文件结构
entry/src/test/
├── ohosTest/
│ ├── Ability.test.ets # 应用生命周期测试
│ ├── Calculator.test.ets # 业务逻辑测试
│ └── list.test.ets # 测试集合入口
2. 基础测试用例
typescript
import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos.unittest';
describe('CalculatorTests', () => {
let calculator: Calculator;
beforeAll(() => {
calculator = new Calculator();
console.info('测试套件初始化');
});
beforeEach(() => {
calculator.reset();
console.info('每个测试用例前重置计算器');
});
afterEach(() => {
console.info('每个测试用例后的清理');
});
afterAll(() => {
calculator = null;
console.info('所有测试完成');
});
it('add_positive_numbers_should_return_correct_sum', 0, () => {
const result = calculator.add(1, 2);
expect(result).assertEqual(3);
});
it('divide_by_zero_should_throw_error', 0, () => {
expect(() => {
calculator.divide(10, 0);
}).assertThrow('Cannot divide by zero');
});
it('string_concat_verify', 0, () => {
const str = 'Hello' + ' ' + 'HarmonyOS';
expect(str).assertEqual('Hello HarmonyOS');
});
});
```
### 3. 异步测试
鸿蒙很多 API 都是异步的,测试框架也提供了对异步的支持:
```typescript
import { describe, it, expect } from '@ohos.unittest';
describe('AsyncTests', () => {
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
it('async_data_fetch_should_return_data', 0, async () => {
const data = await fetchDataFromServer();
expect(data).not().assertNull();
expect(data.code).assertEqual(200);
});
it('parallel_tasks_should_complete', 0, async () => {
const results = await Promise.all([
task1(),
task2(),
task3()
]);
expect(results.length).assertEqual(3);
results.forEach(r => expect(r).assertTrue());
});
});
```
### 4. Mock 数据测试
```typescript
import { describe, it, expect } from '@ohos.unittest';
// Mock 用户数据
const mockUser = {
id: 1,
name: 'TestUser',
age: 25,
roles: ['developer', 'tester']
};
describe('UserServiceTests', () => {
it('user_should_have_valid_role', 0, () => {
const service = new UserService(mockUser);
const hasRole = service.hasRole('developer');
expect(hasRole).assertTrue();
});
it('user_age_should_be_positive', 0, () => {
expect(mockUser.age).assertLarger(0);
expect(mockUser.age).assertLess(150);
});
});
```
## 三、UI 自动化测试
UI 自动化测试通过 `@ohos.test.uitest` 框架驱动,可以模拟用户的操作行为。
### 1. 配置准备
在 `entry/build-profile.json5` 中开启测试:
```json
{
"buildOption": {
"testOptions": {
"uitest": {
"enabled": true,
"packageName": "com.example.myapp"
}
}
}
}
2. 编写 UI 测试
typescript
import { Driver, ON, Component } from '@ohos.test.uitest';
import { describe, it, expect } from '@ohos.unittest';
describe('LoginUITests', () => {
let driver: Driver;
beforeAll(() => {
driver = await Driver.create();
await driver.delayMs(2000); // 等待页面加载
});
it('login_with_valid_credentials_should_succeed', 0, async () => {
// 查找用户名输入框
const usernameInput = await driver.findComponent(ON.text('请输入用户名'));
await usernameInput.inputText('admin');
// 查找密码输入框
const passwordInput = await driver.findComponent(ON.text('请输入密码'));
await passwordInput.inputText('123456');
// 点击登录按钮
const loginBtn = await driver.findComponent(ON.text('登录'));
await loginBtn.click();
// 等待跳转
await driver.delayMs(3000);
// 验证跳转到主页
const title = await driver.findComponent(ON.text('首页'));
expect(title).not().assertNull();
});
it('empty_username_should_show_error', 0, async () => {
const loginBtn = await driver.findComponent(ON.text('登录'));
await loginBtn.click();
await driver.delayMs(1000);
// 验证错误提示
const errorTip = await driver.findComponent(ON.text('请输入用户名'));
expect(errorTip).not().assertNull();
});
});
3. 滑动与手势测试
typescript
it('scroll_to_bottom_and_load_more', 0, async () => {
const scrollList = await driver.findComponent(ON.id('scrollable_list'));
// 向下滑动加载更多
for (let i = 0; i < 3; i++) {
await scrollList.scrollDown();
await driver.delayMs(1000);
}
// 验证加载更多按钮出现
const loadMore = await driver.findComponent(ON.text('点击加载更多'), 5000);
expect(loadMore).not().assertNull();
});
it('long_press_should_show_context_menu', 0, async () => {
const item = await driver.findComponent(ON.text('待办事项1'));
await item.longClick();
await driver.delayMs(1500);
const menu = await driver.findComponent(ON.text('删除'));
expect(menu).not().assertNull();
});
4. 多设备测试
typescript
describe('MultiDeviceTests', () => {
it('distributed_photo_browser', 0, async () => {
// 连接设备
const devices = await Driver.getDeviceList();
expect(devices.length).assertLarger(1);
const phoneDriver = devices[0];
const tabletDriver = devices[1];
// 在手机上选照片
const photo = await phoneDriver.findComponent(ON.text('风景照.jpg'));
await photo.click();
// 验证平板上同步显示
await tabletDriver.delayMs(2000);
const photoOnTablet = await tabletDriver.findComponent(ON.text('风景照.jpg'), 5000);
expect(photoOnTablet).not().assertNull();
});
});
四、性能测试
使用 DevEco Studio 的 SmartPerf 工具进行性能监控:
typescript
import { perf } from '@ohos.perf';
it('measure_list_scroll_performance', 0, async () => {
const metrics = perf.start('ListScroll');
const driver = await Driver.create();
const list = await driver.findComponent(ON.id('item_list'));
// 连续滑动 50 次
for (let i = 0; i < 50; i++) {
await list.scrollDown();
}
const result = perf.stop(metrics);
console.info(`FPS: ${result.fps}`);
console.info(`帧率稳定度: ${result.stability}%`);
console.info(`内存峰值: ${result.memoryPeak}MB`);
expect(result.fps).assertLarger(55); // 保证流畅
expect(result.memoryLeak).assertFalse(); // 无内存泄漏
});
五、最佳实践
1. 测试金字塔
/\
/E2E\ ← 少量端到端测试
/ \
/ 集成 \ ← 适量集成测试
/ \
/ 单元测试 \ ← 大量单元测试
/______________\
2. 测试覆盖率目标
| 层级 | 覆盖率目标 | 说明 |
|---|---|---|
| 核心业务逻辑 | ≥ 90% | 算法、数据处理 |
| UI 组件 | ≥ 70% | 组件渲染、交互 |
| 页面流程 | ≥ 60% | 页面跳转、路由 |
| 整体项目 | ≥ 80% | 全量统计 |
3. CI/CD 集成
在 DevEco Studio 中配置自动测试:
bash
# 命令行运行测试
hvigorw --mode ohosTest -p module=entry@ohosTest
# 生成测试报告
hvigorw --mode ohosTest -p module=entry@ohosTest -p coverage=true
4. 常见陷阱
- 避免测试耦合:每个测试用例应独立运行
- 合理使用 timeout:异步测试一定要设超时,默认 5000ms
- 测试数据隔离:不用生产数据,使用 mock 或 fixture
- 关注边界条件:空值、超长字符串、并发请求等
结语
鸿蒙的测试框架设计合理、文档齐全,从单元测试到 UI 自动化再到性能测试,覆盖了应用质量保障的全流程。建议在项目中尽早引入测试,遵循"测试金字塔"原则,合理分配不同类型测试的比例。好的测试习惯会让你的鸿蒙应用更加稳定可靠。记住:没有测试的代码,就是遗留代码。