鸿蒙5 UI自动化测试:DevEco Studio测试脚本编写实战指南

暗雨OL
发布于 2025-6-30 02:42
浏览
0收藏

一、鸿蒙5 UI自动化测试概述
鸿蒙5在ArkCompiler的支持下,提供了全新的UI自动化测试框架,主要特性包括:

​​跨语言支持​​:兼容JS/TS测试脚本
​​多设备适配​​:自动适配不同屏幕尺寸
​​智能识别​​:支持ID、文本、类型等多种元素定位方式
​​集成报告​​:生成详细的测试执行报告
二、测试环境配置

  1. 创建测试模块
    在DevEco Studio中:

右键项目 → New → Module
选择 “Test” 模块类型
配置测试模块名称(如 “test”)
2. 测试目录结构
test/
├── src/
│ ├── main/
│ │ ├── resources/
│ │ └── module.json5
│ └── test/
│ ├── js/
│ │ └── test/
│ │ └── Example.test.ets
│ └── resources/
└── oh-package.json5
3. 配置测试依赖
oh-package.json5:

{
“devDependencies”: {
“@ohos/hypium”: “^1.0.0”
}
}
三、基础测试脚本编写

  1. 测试用例结构
    // Example.test.ets
    import { describe, it, expect } from ‘@ohos/hypium’;
    import abilityTest from ‘@ohos/abilityTest’;

export default function testRunner() {
describe(‘AppTest’, function() {
it(‘StartAbility’, 0, async function() {
try {
await abilityTest.startAbility({
bundleName: ‘com.example.myapp’,
abilityName: ‘EntryAbility’
});
expect(true).assertTrue();
} catch (err) {
expect(err).assertNull();
}
});
});
}
2. 元素定位与操作
import { by, Component, ON } from ‘@ohos/hypium’;

it(‘ButtonClickTest’, 0, async function() {
// 通过ID定位按钮
const submitBtn = await ON.component(by.id(‘submit_button’));

// 验证元素存在
expect(await submitBtn.isDisplayed()).assertTrue();

// 点击操作
await submitBtn.click();

// 验证结果
const resultText = await ON.component(by.text(‘操作成功’));
expect(await resultText.isDisplayed()).assertTrue();
});
四、高级测试技术

  1. 页面跳转测试
    it(‘NavigationTest’, 0, async function() {
    // 初始页面验证
    expect(await ON.component(by.id(‘home_page’)).isDisplayed()).assertTrue();

// 跳转到详情页
await ON.component(by.id(‘detail_btn’)).click();

// 等待页面加载
await ON.delay(1000);

// 验证详情页元素
expect(await ON.component(by.id(‘detail_page’)).isDisplayed()).assertTrue();

// 返回首页
await ON.component(by.id(‘back_btn’)).click();

// 验证返回结果
expect(await ON.component(by.id(‘home_page’)).isDisplayed()).assertTrue();
});
2. 列表操作测试
it(‘ListScrollTest’, 0, async function() {
// 获取列表组件
const newsList = await ON.component(by.id(‘news_list’));

// 验证列表存在
expect(await newsList.isDisplayed()).assertTrue();

// 滚动列表
await newsList.scrollToEnd();

// 获取列表项数量
const items = await newsList.findComponents(by.type(‘ListItem’));
expect(items.length).assertLarger(5);

// 点击最后一项
await items[items.length - 1].click();

// 验证详情页打开
expect(await ON.component(by.id(‘news_detail’)).isDisplayed()).assertTrue();
});
3. 输入操作测试
it(‘InputTest’, 0, async function() {
// 定位输入框
const usernameInput = await ON.component(by.id(‘username_input’));

// 输入文本
await usernameInput.inputText(‘testuser’);

// 验证输入内容
expect(await usernameInput.getText()).assertEqual(‘testuser’);

// 定位密码框
const passwordInput = await ON.component(by.id(‘password_input’));

// 输入密码
await passwordInput.inputText(‘123456’);

// 点击登录
await ON.component(by.id(‘login_btn’)).click();

// 验证登录结果
expect(await ON.component(by.text(‘登录成功’)).isDisplayed()).assertTrue();
});
五、测试数据驱动

  1. 参数化测试
    const testCases = [
    { username: ‘admin’, password: ‘admin123’, expected: true },
    { username: ‘test’, password: ‘wrong’, expected: false },
    { username: ‘’, password: ‘’, expected: false }
    ];

testCases.forEach((caseData, index) => {
it(LoginTest_${index}, 0, async function() {
// 输入用户名
await ON.component(by.id(‘username_input’)).inputText(caseData.username);

// 输入密码
await ON.component(by.id('password_input')).inputText(caseData.password);

// 点击登录
await ON.component(by.id('login_btn')).click();

// 验证结果
if (caseData.expected) {
  expect(await ON.component(by.text('登录成功')).isDisplayed()).assertTrue();
} else {
  expect(await ON.component(by.text('登录失败')).isDisplayed()).assertTrue();
}

// 返回登录页
if (await ON.component(by.id('back_btn')).isDisplayed()) {
  await ON.component(by.id('back_btn')).click();
}

});
});
2. 外部数据文件
test/resources/login_data.json:

[
{ “username”: “user1”, “password”: “pass1”, “expected”: true },
{ “username”: “user2”, “password”: “pass2”, “expected”: false }
]
测试脚本中使用:

import data from ‘…/…/resources/login_data.json’;

data.forEach((caseData, index) => {
it(DataDrivenLogin_${index}, 0, async function() {
// 测试逻辑同上
});
});
六、测试套件组织

  1. 测试套件定义
    // SmokeTestSuite.ets
    import { describe } from ‘@ohos/hypium’;

export default function smokeTestSuite() {
describe(‘SmokeTest’, function() {
require(‘./LoginTest.ets’);
require(‘./HomePageTest.ets’);
require(‘./ProfileTest.ets’);
});
}

// RegressionTestSuite.ets
import { describe } from ‘@ohos/hypium’;

export default function regressionTestSuite() {
describe(‘RegressionTest’, function() {
require(‘./FullFlowTest.ets’);
require(‘./PerformanceTest.ets’);
require(‘./CompatibilityTest.ets’);
});
}
2. 主测试入口
test/src/test/js/test/index.ets:

import smokeTest from ‘./SmokeTestSuite’;
import regressionTest from ‘./RegressionTestSuite’;

export default function mainTestRunner() {
smokeTest();
regressionTest();
}
七、高级技巧与最佳实践

  1. 自定义匹配器
    import { Matcher } from ‘@ohos/hypium’;

class CustomMatcher extends Matcher {
async toBeVisible(component: Component) {
const isDisplayed = await component.isDisplayed();
return {
pass: isDisplayed,
message: Expected component to ${this.isNot ? 'not ' : ''}be visible
};
}
}

// 注册自定义匹配器
expect.extend(new CustomMatcher());

// 使用示例
it(‘CustomMatcherTest’, 0, async function() {
const btn = await ON.component(by.id(‘my_button’));
await expect(btn).toBeVisible();
});
2. 页面对象模式
test/src/test/js/test/pages/LoginPage.ets:

export default class LoginPage {
static async inputUsername(text: string) {
await ON.component(by.id(‘username_input’)).inputText(text);
}

static async inputPassword(text: string) {
await ON.component(by.id(‘password_input’)).inputText(text);
}

static async clickLogin() {
await ON.component(by.id(‘login_btn’)).click();
}

static async isLoginSuccess() {
return await ON.component(by.text(‘登录成功’)).isDisplayed();
}
}

// 测试脚本中使用
it(‘LoginWithPageObject’, 0, async function() {
await LoginPage.inputUsername(‘admin’);
await LoginPage.inputPassword(‘admin123’);
await LoginPage.clickLogin();
expect(await LoginPage.isLoginSuccess()).assertTrue();
});
3. 测试钩子
import { beforeAll, afterEach, afterAll } from ‘@ohos/hypium’;

describe(‘AuthTest’, function() {
beforeAll(async function() {
// 测试前置操作
await abilityTest.startAbility({
bundleName: ‘com.example.myapp’,
abilityName: ‘EntryAbility’
});
});

afterEach(async function() {
// 每个测试用例后执行
await ON.component(by.id(‘logout_btn’)).click();
});

afterAll(function() {
// 所有测试完成后执行
console.log(‘AuthTest completed’);
});

// 测试用例…
});
八、测试报告与调试

  1. 生成测试报告
    运行测试命令:

hdc shell aa test -b com.example.myapp -m test -s unittest OpenHarmonyTestRunner -s class com.example.myapp.test.Runner
测试报告位置:

/data/test/runner/resource/report/
2. 测试截图
it(‘ScreenshotTest’, 0, async function() {
// 执行操作…
await ON.component(by.id(‘some_button’)).click();

// 截图
const screenshotPath = ‘/data/test/screenshots/’;
await ON.captureScreen(screenshotPath + ‘after_click.png’);

// 验证截图存在
const file = await fileIo.open(screenshotPath + ‘after_click.png’);
expect(file).not().assertNull();
file.close();
});
3. 测试日志
import { Logger } from ‘@ohos/hypium’;

it(‘LoggingTest’, 0, async function() {
Logger.info(‘Starting login test’);

try {
await LoginPage.inputUsername(‘testuser’);
Logger.debug(‘Username entered’);

await LoginPage.inputPassword('testpass');
Logger.debug('Password entered');

await LoginPage.clickLogin();
Logger.info('Login button clicked');

expect(await LoginPage.isLoginSuccess()).assertTrue();

} catch (err) {
Logger.error(Test failed: ${err});
throw err;
}
});
九、持续集成配置

  1. GitHub Actions 示例
    .github/workflows/test.yml:

name: UI Automation Test

on: [push, pull_request]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

  - name: Setup Node.js
    uses: actions/setup-node@v2
    with:
      node-version: '14'
  
  - name: Install DevEco CLI
    run: npm install -g @ohos/deveco-cli
  
  - name: Run Tests
    run: |
      deveco test --module test --device emulator
  
  - name: Upload Reports
    uses: actions/upload-artifact@v2
    with:
      name: test-reports
      path: test/build/reports/
  1. 本地测试命令

运行单个测试模块

deveco test --module test --device emulator

运行特定测试套件

deveco test --module test --device emulator --suite SmokeTest

生成HTML报告

deveco test --report html
十、总结与最佳实践

  1. 测试金字塔实践
    ​​单元测试​​:覆盖核心业务逻辑
    ​​组件测试​​:验证UI组件行为
    ​​UI自动化测试​​:覆盖关键用户流程
  2. 测试设计原则
    ​​原子性​​:每个测试只验证一个功能点
    ​​独立性​​:测试之间不依赖执行顺序
    ​​可重复性​​:在任何环境都能稳定运行
    ​​可读性​​:清晰的测试命名和结构
  3. 性能优化建议
    使用beforeAll共享初始化操作
    避免不必要的UI操作
    合理使用delay等待元素出现
    并行执行独立测试用例
    鸿蒙5的UI自动化测试框架结合ArkCompiler的优化,为开发者提供了强大的测试能力。通过本文介绍的技术方案,开发者可以构建出高效、可靠的自动化测试套件,确保应用质量。随着鸿蒙生态的发展,这套测试方案将支持更丰富的测试场景和设备类型。

分类
标签
收藏
回复
举报
回复
    相关推荐