测试是软件工程中不可或缺的环节。在 HarmonyOS 应用开发中,良好的测试策略能够有效提升代码质量、减少线上故障、加速迭代节奏。本文将系统性地介绍 HarmonyOS 应用的单元测试与 UI 自动化测试实战,涵盖测试框架选型、用例编写、Mock 技巧、UI 交互验证以及持续集成接入,帮助开发者建立完整的测试体系。
HarmonyOS 应用测试遵循经典的测试金字塔模型:
- 单元测试(Unit Test):测试单个函数、类或模块的逻辑正确性,执行速度快,占比最高 - 集成测试(Integration Test):测试模块间协作、数据流转、API 调用等场景 - UI 自动化测试(UI Test):模拟用户操作,验证界面交互、页面跳转、状态更新等端到端流程
HarmonyOS 官方提供了完整的测试框架支持:
- ArkTS 单元测试框架:基于 Jest 风格的测试 API,支持同步与异步断言 - UI 测试框架(UiTest):提供组件查找、事件注入、属性断言等能力 - 测试运行器(Test Runner):集成在 DevEco Studio 中,支持一键运行与调试
在 DevEco Studio 创建的工程中,测试代码默认位于 `entry/src/ohosTest/ets/test` 目录。测试文件命名遵循 `*.test.ets` 规范。
典型的测试用例结构:
import { describe, it, expect } from '@ohos/hypium'export default function abilityTest() { describe('UserService', () => { it('should return user info when login success', async () => { const service = new UserService() const result = await service.login('test', 'password') expect(result.success).assertTrue() expect(result.userName).assertEqual('test') }) }) }
针对业务逻辑层(如数据处理、状态计算、工具函数),编写纯函数风格的单元测试:
// utils/StringUtil.ets
export class StringUtil {
static truncate(text: string, maxLength: number): string {
if (text.length <= maxLength) return text
return text.substring(0, maxLength) + '...'
}static isEmail(email: string): boolean { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) } }
// test/StringUtil.test.ets import { describe, it, expect } from '@ohos/hypium' import { StringUtil } from '../../../ets/utils/StringUtil'
export default function stringUtilTest() { describe('StringUtil', () => { it('should truncate long text', () => { const result = StringUtil.truncate('Hello World', 5) expect(result).assertEqual('Hello...') })
it('should not truncate short text', () => { const result = StringUtil.truncate('Hi', 5) expect(result).assertEqual('Hi') })
it('should validate email format', () => { expect(StringUtil.isEmail('user@example.com')).assertTrue() expect(StringUtil.isEmail('invalid-email')).assertFalse() }) }) }
对于网络请求、数据库操作等异步场景,使用 `async/await` 配合 `done` 回调:
import { describe, it, expect } from '@ohos/hypium'
import { http } from '@kit.NetworkKit'export default function httpTest() { describe('NetworkService', () => { it('should fetch user list from API', async (done) => { const request = http.createHttp() try { const response = await request.request('https://api.example.com/users') const data = JSON.parse(response.result as string) expect(data.code).assertEqual(200) expect(data.users.length).assertLarger(0) done() } catch (err) { expect().assertFail() done() } }) }) }
当测试代码依赖外部服务(如网络、数据库)时,通过 Mock 技术隔离外部依赖:
// service/UserRepository.ets
export interface IUserRepository {
getUserById(id: string): Promise
}export class UserRepository implements IUserRepository { async getUserById(id: string): Promise { // 真实实现调用网络或数据库 return await http.get(`/users/${id}`) } }
// test/UserViewModel.test.ets class MockUserRepository implements IUserRepository { async getUserById(id: string): Promise { return { id, name: 'Mock User', email: 'mock@test.com' } } }
export default function viewModelTest() { describe('UserViewModel', () => { it('should load user info', async () => { const mockRepo = new MockUserRepository() const viewModel = new UserViewModel(mockRepo) await viewModel.loadUser('123') expect(viewModel.userName).assertEqual('Mock User') }) }) }
对于使用 `@State`、`@Observed` 的状态驱动逻辑,可以测试状态变更是否触发预期行为:
@Observed
class CounterState {
count: number = 0increment() { this.count++ }
decrement() { this.count-- } }
export default function stateTest() { describe('CounterState', () => { it('should increment count', () => { const state = new CounterState() state.increment() expect(state.count).assertEqual(1) })
it('should decrement count', () => { const state = new CounterState() state.count = 5 state.decrement() expect(state.count).assertEqual(4) }) }) }
HarmonyOS 的 UI 测试框架(UiTest)提供了组件查找、事件模拟、属性断言等能力。核心 API:
- `Driver.create()`:创建测试驱动器 - `driver.findComponent(on)`:查找组件 - `component.click()`:模拟点击 - `component.inputText(text)`:输入文本 - `component.getText()`:获取文本内容
通过 `id`、`text`、`type` 等属性定位组件并验证状态:
import { describe, it, expect } from '@ohos/hypium'
import { Driver, ON } from '@ohos.UiTest'export default function loginPageTest() { describe('LoginPage', () => { it('should display login button', async () => { const driver = Driver.create() const loginBtn = await driver.findComponent(ON.text('登录')) expect(await loginBtn.isEnabled()).assertTrue() })
it('should show error when submit empty form', async () => { const driver = Driver.create() const submitBtn = await driver.findComponent(ON.id('submitButton')) await submitBtn.click() const errorMsg = await driver.findComponent(ON.text('请输入用户名')) expect(await errorMsg.isVisible()).assertTrue() }) }) }
模拟用户输入、点击、滑动等操作:
export default function formTest() {
describe('RegisterForm', () => {
it('should submit form with valid data', async () => {
const driver = Driver.create()
// 输入用户名
const usernameInput = await driver.findComponent(ON.id('usernameInput'))
await usernameInput.inputText('testuser')
// 输入密码
const passwordInput = await driver.findComponent(ON.id('passwordInput'))
await passwordInput.inputText('password123')
// 点击提交
const submitBtn = await driver.findComponent(ON.id('submitButton'))
await submitBtn.click()
// 验证成功提示
await driver.delayMs(1000) // 等待网络请求
const successMsg = await driver.findComponent(ON.text('注册成功'))
expect(await successMsg.isVisible()).assertTrue()
})
})
}对于 List、Grid 等可滚动组件,测试滚动行为与数据加载:
export default function listTest() {
describe('UserList', () => {
it('should load more data when scroll to bottom', async () => {
const driver = Driver.create()
const list = await driver.findComponent(ON.type('List'))
// 获取初始条目数
const initialItems = await driver.findComponents(ON.type('ListItem'))
const initialCount = initialItems.length
// 滚动到底部
await list.scrollToBottom()
await driver.delayMs(1000)
// 验证条目增加
const updatedItems = await driver.findComponents(ON.type('ListItem'))
expect(updatedItems.length).assertLarger(initialCount)
})
})
}验证页面导航与参数传递:
export default function navigationTest() {
describe('PageNavigation', () => {
it('should navigate to detail page with correct params', async () => {
const driver = Driver.create()
// 点击列表第一项
const firstItem = await driver.findComponent(ON.id('listItem_0'))
await firstItem.click()
// 验证跳转到详情页
await driver.delayMs(500)
const detailTitle = await driver.findComponent(ON.id('detailTitle'))
const titleText = await detailTitle.getText()
expect(titleText).assertContain('详情')
})
})
}测试 Dialog、AlertDialog 等弹窗组件:
export default function dialogTest() {
describe('DeleteConfirmDialog', () => {
it('should close dialog when click cancel', async () => {
const driver = Driver.create()
// 触发删除操作
const deleteBtn = await driver.findComponent(ON.id('deleteButton'))
await deleteBtn.click()
// 验证弹窗显示
const dialog = await driver.findComponent(ON.text('确认删除'))
expect(await dialog.isVisible()).assertTrue()
// 点击取消
const cancelBtn = await driver.findComponent(ON.text('取消'))
await cancelBtn.click()
// 验证弹窗消失
await driver.delayMs(300)
expect(await dialog.isVisible()).assertFalse()
})
})
}- 单一职责:每个测试用例只验证一个行为或场景 - 可重复性:测试结果不依赖执行顺序或外部状态 - 可读性:使用清晰的命名和注释,让测试意图一目了然 - 独立性:测试用例之间互不依赖,可以并行执行
- 核心业务逻辑:覆盖率 ≥ 80% - 工具类与公共模块:覆盖率 ≥ 90% - UI 关键路径:登录、支付、订单等核心流程必须有 UI 自动化测试
将测试集成到 CI/CD 流程中,在代码提交或合并时自动运行:
# 命令行运行测试
hdc shell aa test -b com.example.app -m entry_test -s unittest OpenHarmonyTestRunner# 在 CI 脚本中集成 npm run test npm run test:ui
- 使用测试专用数据源:避免污染生产数据 - 测试后清理数据:在 `afterEach` 或 `afterAll` 中重置状态 - 使用数据驱动测试:通过参数化测试覆盖多种输入场景
const testCases = [
{ input: 'user@example.com', expected: true },
{ input: 'invalid-email', expected: false },
{ input: '', expected: false }
]testCases.forEach(({ input, expected }) => { it(`should validate email: ${input}`, () => { expect(StringUtil.isEmail(input)).assertEqual(expected) }) })
问题:异步测试用例超时失败。
解决方案: - 使用 `done()` 回调明确标记测试完成 - 增加超时时间配置 - 检查异步逻辑是否有未捕获的异常
问题:UI 测试偶现失败,无法稳定复现。
解决方案: - 添加合理的 `delayMs` 等待动画或网络完成 - 使用 `waitForComponent` 等待组件出现 - 避免依赖绝对坐标,使用 `id` 或 `text` 定位
问题:Mock 测试通过,但真实环境出现问题。
解决方案: - Mock 数据尽量贴近真实场景 - 补充集成测试,使用真实接口验证 - 定期更新 Mock 数据结构
本文系统介绍了 HarmonyOS 应用的单元测试与 UI 自动化测试实战,涵盖测试框架、用例编写、Mock 技巧、UI 交互验证等核心内容。通过建立完整的测试体系,开发者可以显著提升代码质量、减少线上故障、加速迭代节奏。
测试不是开发的负担,而是质量的保障。在实际项目中,建议从核心业务逻辑的单元测试入手,逐步补充 UI 自动化测试覆盖关键路径,并将测试集成到 CI/CD 流程中,让测试成为开发流程的有机组成部分。
下一步,我们将探讨 HarmonyOS 应用的安全加固与数据保护实战,敬请期待。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。