鸿蒙应用测试实战:从单元测试到自动化测试

引言

测试是保证应用质量的关键环节。鸿蒙(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 自动化再到性能测试,覆盖了应用质量保障的全流程。建议在项目中尽早引入测试,遵循"测试金字塔"原则,合理分配不同类型测试的比例。好的测试习惯会让你的鸿蒙应用更加稳定可靠。记住:没有测试的代码,就是遗留代码。

相关推荐
Davina_yu2 小时前
Hello HarmonyOS:搭建DevEco Studio开发环境与第一个应用运行(1)
harmonyos·鸿蒙原生开发
2501_919749032 小时前
鸿蒙 Flutter 实战:video_compress 3.1.4 适配 3.27-ohos 全流程
flutter·华为·harmonyos
nashane2 小时前
HarmonyOS 6学习:应用退出动画优化实战——从“闪退“到优雅退出的完美蜕变
学习·华为·harmonyos
程序猿追5 小时前
在 HarmonyOS 模拟器上用递归种出科赫分形
华为·harmonyos
高心星5 小时前
鸿蒙6.0应用开发——访问应用文件
华为·文件读写·fs·鸿蒙6.0·harmonyos6.0·应用文件·fileio
FrameNotWork5 小时前
HarmonyOS三方库:lv-markdown-in 技术解析与自定义语法扩展实战
华为·harmonyos
条tiao条7 小时前
鸿蒙 ArkTS 实战进阶:从核心组件到面向对象编程一篇通
华为·harmonyos
book01217 小时前
华为ensp学习日志 记2026
学习·华为·智能路由器