1# 自动化测试框架使用介绍 2 3## 简介 4 OpenHarmony自动化测试框架代码部件仓arkXtest,包含单元测试框架(JsUnit)和Ui测试框架(UiTest)。 5 6 单元测试框架(JsUnit)提供单元测试用例执行能力,提供用例编写基础接口,生成对应报告,用于测试系统或应用接口。 7 8 Ui测试框架(UiTest)通过简洁易用的API提供查找和操作界面控件能力,支持用户开发基于界面操作的自动化测试脚本。 9 10## 目录 11 12``` 13arkXtest 14 |-----jsunit 单元测试框架 15 |-----uitest Ui测试框架 16``` 17## 约束限制 18本模块首批接口从API version 8开始支持。后续版本的新增接口,采用上角标单独标记接口的起始版本。 19 20## 单元测试框架功能特性 21 22| No. | 特性 | 功能说明 | 23| ---- | -------- | ------------------------------------------------------------ | 24| 1 | 基础流程 | 支持编写及异步执行基础用例。 | 25| 2 | 断言库 | 判断用例实际期望值与预期值是否相符。 | 26| 3 | Mock能力 | 支持函数级mock能力,对定义的函数进行mock后修改函数的行为,使其返回指定的值或者执行某种动作。 | 27| 4 | 数据驱动 | 提供数据驱动能力,支持复用同一个测试脚本,使用不同输入数据驱动执行。 | 28| 5 | 专项能力 | 支持测试套与用例筛选、随机执行、压力测试、超时设置、遇错即停模式,跳过,支持测试套嵌套等。 | 29 30### 使用说明 31 32#### 基础流程 33 34测试用例采用业内通用语法,describe代表一个测试套, it代表一条用例。 35 36| No. | API | 功能说明 | 37|-----| ----------------- |------------------------------------------------------------------------| 38| 1 | describe | 定义一个测试套,支持两个参数:测试套名称和测试套函数。 | 39| 2 | beforeAll | 在测试套内定义一个预置条件,在所有测试用例开始前执行且仅执行一次,支持一个参数:预置动作函数。 | 40| 3 | beforeEach | 在测试套内定义一个单元预置条件,在每条测试用例开始前执行,执行次数与it定义的测试用例数一致,支持一个参数:预置动作函数。 | 41| 4 | afterEach | 在测试套内定义一个单元清理条件,在每条测试用例结束后执行,执行次数与it定义的测试用例数一致,支持一个参数:清理动作函数。 | 42| 5 | afterAll | 在测试套内定义一个清理条件,在所有测试用例结束后执行且仅执行一次,支持一个参数:清理动作函数。 | 43| 6 | beforeItSpecified | @since1.0.15在测试套内定义一个单元预置条件,仅在指定测试用例开始前执行,支持两个参数:单个用例名称或用例名称数组、预置动作函数。 | 44| 7 | afterItSpecified | @since1.0.15在测试套内定义一个单元清理条件,仅在指定测试用例结束后执行,支持两个参数:单个用例名称或用例名称数组、清理动作函数 | 45| 8 | it | 定义一条测试用例,支持三个参数:用例名称,过滤参数和用例函数。 | 46| 9 | expect | 支持bool类型判断等多种断言方法。 | 47| 10 | xdescribe | @since1.0.17定义一个跳过的测试套,支持两个参数:测试套名称和测试套函数。 | 48| 11 | xit | @since1.0.17定义一条跳过的测试用例,支持三个参数:用例名称,过滤参数和用例函数。 | 49 50 51beforeItSpecified, afterItSpecified 示例代码: 52 53```javascript 54import { describe, it, expect, beforeItSpecified, afterItSpecified } from '@ohos/hypium'; 55export default function beforeItSpecifiedTest() { 56 describe('beforeItSpecifiedTest', () => { 57 beforeItSpecified(['String_assertContain_success'], () => { 58 const num:number = 1; 59 expect(num).assertEqual(1); 60 }) 61 afterItSpecified(['String_assertContain_success'], async (done: Function) => { 62 const str:string = 'abc'; 63 setTimeout(()=>{ 64 try { 65 expect(str).assertContain('b'); 66 } catch (error) { 67 console.error(`error message ${JSON.stringify(error)}`); 68 } 69 done(); 70 }, 1000) 71 }) 72 it('String_assertContain_success', 0, () => { 73 let a: string = 'abc'; 74 let b: string = 'b'; 75 expect(a).assertContain(b); 76 expect(a).assertEqual(a); 77 }) 78 }) 79} 80``` 81 82#### 断言库 83 84##### 断言功能列表 85 86 87| No. | API | 功能说明 | 88| :--- | :------------------| ------------------------------------------------------------ | 89| 1 | assertClose | 检验actualvalue和expectvalue(0)的接近程度是否是expectValue(1)。 | 90| 2 | assertContain | 检验actualvalue中是否包含expectvalue。 | 91| 3 | assertEqual | 检验actualvalue是否等于expectvalue[0]。 | 92| 4 | assertFail | 抛出一个错误。 | 93| 5 | assertFalse | 检验actualvalue是否是false。 | 94| 6 | assertTrue | 检验actualvalue是否是true。 | 95| 7 | assertInstanceOf | 检验actualvalue是否是expectvalue类型,支持基础类型。 | 96| 8 | assertLarger | 检验actualvalue是否大于expectvalue。 | 97| 9 | assertLess | 检验actualvalue是否小于expectvalue。 | 98| 10 | assertNull | 检验actualvalue是否是null。 | 99| 11 | assertThrowError | 检验actualvalue抛出Error内容是否是expectValue。 | 100| 12 | assertUndefined | 检验actualvalue是否是undefined。 | 101| 13 | assertNaN | @since1.0.4 检验actualvalue是否是一个NAN | 102| 14 | assertNegUnlimited | @since1.0.4 检验actualvalue是否等于Number.NEGATIVE_INFINITY | 103| 15 | assertPosUnlimited | @since1.0.4 检验actualvalue是否等于Number.POSITIVE_INFINITY | 104| 16 | assertDeepEquals | @since1.0.4 检验actualvalue和expectvalue是否完全相等 | 105| 17 | assertPromiseIsPending | @since1.0.4 判断promise是否处于Pending状态。 | 106| 18 | assertPromiseIsRejected | @since1.0.4 判断promise是否处于Rejected状态。 | 107| 19 | assertPromiseIsRejectedWith | @since1.0.4 判断promise是否处于Rejected状态,并且比较执行的结果值。 | 108| 20 | assertPromiseIsRejectedWithError | @since1.0.4 判断promise是否处于Rejected状态并有异常,同时比较异常的类型和message值。 | 109| 21 | assertPromiseIsResolved | @since1.0.4 判断promise是否处于Resolved状态。 | 110| 22 | assertPromiseIsResolvedWith | @since1.0.4 判断promise是否处于Resolved状态,并且比较执行的结果值。 | 111| 23 | not | @since1.0.4 断言取反,支持上面所有的断言功能 | 112| 24 | message | @since1.0.17自定义断言异常信息 | 113 114expect断言示例代码: 115 116```javascript 117import { describe, it, expect } from '@ohos/hypium'; 118 119export default function expectTest() { 120 describe('expectTest', () => { 121 it('assertBeClose_success', 0, () => { 122 let a: number = 100; 123 let b: number = 0.1; 124 expect(a).assertClose(99, b); 125 }) 126 it('assertInstanceOf_success', 0, () => { 127 let a: string = 'strTest'; 128 expect(a).assertInstanceOf('String'); 129 }) 130 it('assertNaN_success', 0, () => { 131 expect(Number.NaN).assertNaN(); // true 132 }) 133 it('assertNegUnlimited_success', 0, () => { 134 expect(Number.NEGATIVE_INFINITY).assertNegUnlimited(); // true 135 }) 136 it('assertPosUnlimited_success', 0, () => { 137 expect(Number.POSITIVE_INFINITY).assertPosUnlimited(); // true 138 }) 139 it('not_number_true', 0, () => { 140 expect(1).not().assertLargerOrEqual(2); 141 }) 142 it('not_number_true_1', 0, () => { 143 expect(3).not().assertLessOrEqual(2); 144 }) 145 it('not_NaN_true', 0, () => { 146 expect(3).not().assertNaN(); 147 }) 148 it('not_contain_true', 0, () => { 149 let a: string = "abc"; 150 let b: string = "cdf"; 151 expect(a).not().assertContain(b); 152 }) 153 it('not_large_true', 0, () => { 154 expect(3).not().assertLarger(4); 155 }) 156 it('not_less_true', 0, () => { 157 expect(3).not().assertLess(2); 158 }) 159 it('not_undefined_true', 0, () => { 160 expect(3).not().assertUndefined(); 161 }) 162 it('deepEquals_null_true', 0, () => { 163 // Defines a variety of assertion methods, which are used to declare expected boolean conditions. 164 expect(null).assertDeepEquals(null); 165 }) 166 it('deepEquals_array_not_have_true', 0, () => { 167 // Defines a variety of assertion methods, which are used to declare expected boolean conditions. 168 const a: Array<number> = []; 169 const b: Array<number> = []; 170 expect(a).assertDeepEquals(b); 171 }) 172 it('deepEquals_map_equal_length_success', 0, () => { 173 // Defines a variety of assertion methods, which are used to declare expected boolean conditions. 174 const a: Map<number, number> = new Map(); 175 const b: Map<number, number> = new Map(); 176 a.set(1, 100); 177 a.set(2, 200); 178 b.set(1, 100); 179 b.set(2, 200); 180 expect(a).assertDeepEquals(b); 181 }) 182 it("deepEquals_obj_success_1", 0, () => { 183 const a: SampleTest = {x: 1}; 184 const b: SampleTest = {x: 1}; 185 expect(a).assertDeepEquals(b); 186 }) 187 it("deepEquals_regExp_success_0", 0, () => { 188 const a: RegExp = new RegExp("/test/"); 189 const b: RegExp = new RegExp("/test/"); 190 expect(a).assertDeepEquals(b); 191 }) 192 it('test_isPending_pass_1', 0, () => { 193 let p: Promise<void> = new Promise<void>(() => {}); 194 expect(p).assertPromiseIsPending(); 195 }) 196 it('test_isRejected_pass_1', 0, () => { 197 let info: PromiseInfo = {res: "no"}; 198 let p: Promise<PromiseInfo> = Promise.reject(info); 199 expect(p).assertPromiseIsRejected(); 200 }) 201 it('test_isRejectedWith_pass_1', 0, () => { 202 let info: PromiseInfo = {res: "reject value"}; 203 let p: Promise<PromiseInfo> = Promise.reject(info); 204 expect(p).assertPromiseIsRejectedWith(info); 205 }) 206 it('test_isRejectedWithError_pass_1', 0, () => { 207 let p1: Promise<TypeError> = Promise.reject(new TypeError('number')); 208 expect(p1).assertPromiseIsRejectedWithError(TypeError); 209 }) 210 it('test_isResolved_pass_1', 0, () => { 211 let info: PromiseInfo = {res: "result value"}; 212 let p: Promise<PromiseInfo> = Promise.resolve(info); 213 expect(p).assertPromiseIsResolved(); 214 }) 215 it('test_isResolvedTo_pass_1', 0, () => { 216 let info: PromiseInfo = {res: "result value"}; 217 let p: Promise<PromiseInfo> = Promise.resolve(info); 218 expect(p).assertPromiseIsResolvedWith(info); 219 }) 220 it("test_message", 0, () => { 221 expect(1).message('1 is not equal 2!').assertEqual(2); // fail 222 }) 223 }) 224} 225 226interface SampleTest { 227 x: number; 228} 229 230interface PromiseInfo { 231 res: string; 232} 233``` 234 235##### 自定义断言@since1.0.18 236 237示例代码: 238 239```javascript 240import { describe, Assert, beforeAll, expect, Hypium, it } from '@ohos/hypium'; 241 242// custom.ets 243interface customAssert extends Assert { 244 // 自定义断言声明 245 myAssertEqual(expectValue: boolean): void; 246} 247 248//自定义断言实现 249let myAssertEqual = (actualValue: boolean, expectValue: boolean) => { 250 interface R { 251 pass: boolean, 252 message: string 253} 254 255let result: R = { 256 pass: true, 257 message: 'just is a msg' 258} 259 260let compare = () => { 261 if (expectValue === actualValue) { 262 result.pass = true; 263 result.message = ''; 264 } else { 265 result.pass = false; 266 result.message = 'expectValue !== actualValue!'; 267 } 268 return result; 269} 270result = compare(); 271return result; 272} 273 274export default function customAssertTest() { 275 describe('customAssertTest', () => { 276 beforeAll(() => { 277 //注册自定义断言,只有先注册才可以使用 278 Hypium.registerAssert(myAssertEqual); 279 }) 280 it('assertContain1', 0, () => { 281 let a = true; 282 let b = true; 283 (expect(a) as customAssert).myAssertEqual(b); 284 Hypium.unregisterAssert(myAssertEqual); 285 }) 286 it('assertContain2', 0, () => { 287 Hypium.registerAssert(myAssertEqual); 288 let a = true; 289 let b = true; 290 (expect(a) as customAssert).myAssertEqual(b); 291 // 注销自定义断言,注销以后就无法使用 292 Hypium.unregisterAssert(myAssertEqual); 293 try { 294 (expect(a) as customAssert).myAssertEqual(b); 295 }catch(e) { 296 expect(e.message).assertEqual("myAssertEqual is unregistered"); 297 } 298 }) 299 }) 300} 301``` 302 303#### Mock能力 304 305##### 约束限制 306 307单元测试框架Mock能力从npm包[1.0.1版本](https://ohpm.openharmony.cn/#/cn/detail/@ohos%2Fhypium)开始支持,需修改源码工程中package.info中配置依赖npm包版本号后使用。 308> - 仅支持mock自定义对象,不支持mock系统API对象。 309> - 不支持mock对象的私有函数。 310- **接口列表:** 311 312| No. | API | 功能说明 | 313| --- | --- | --- | 314| 1 | mockFunc(obj: object, f:function()) | mock某个类的对象obj的函数f,那么需要传两个参数:obj和f,支持使用异步函数(说明:对mock而言原函数实现是同步或异步没太多区别,因为mock并不关注原函数的实现)。 | 315| 2 | when(mockedfunc:function) | 对传入后方法做检查,检查是否被mock并标记过,返回的是一个方法声明。 | 316| 3 | afterReturn(x:value) | 设定预期返回一个自定义的值value,比如某个字符串或者一个promise。 | 317| 4 | afterReturnNothing() | 设定预期没有返回值,即 undefined。 | 318| 5 | afterAction(x:action) | 设定预期返回一个函数执行的操作。 | 319| 6 | afterThrow(x:msg) | 设定预期抛出异常,并指定异常msg。 | 320| 7 | clear() | 用例执行完毕后,进行数据mocker对象的还原处理(还原之后对象恢复被mock之前的功能)。 | 321| 8 | any | 设定用户传任何类型参数(undefined和null除外),执行的结果都是预期的值,使用ArgumentMatchers.any方式调用。 | 322| 9 | anyString | 设定用户传任何字符串参数,执行的结果都是预期的值,使用ArgumentMatchers.anyString方式调用。 | 323| 10 | anyBoolean | 设定用户传任何boolean类型参数,执行的结果都是预期的值,使用ArgumentMatchers.anyBoolean方式调用。 | 324| 11 | anyFunction | 设定用户传任何function类型参数,执行的结果都是预期的值,使用ArgumentMatchers.anyFunction方式调用。 | 325| 12 | anyNumber | 设定用户传任何数字类型参数,执行的结果都是预期的值,使用ArgumentMatchers.anyNumber方式调用。 | 326| 13 | anyObj | 设定用户传任何对象类型参数,执行的结果都是预期的值,使用ArgumentMatchers.anyObj方式调用。 | 327| 14 | matchRegexs(Regex) | 设定用户传任何正则表达式类型参数Regex,执行的结果都是预期的值,使用ArgumentMatchers.matchRegexs(Regex)方式调用。 | 328| 15 | verify(methodName, argsArray) | 验证methodName(函数名字符串)所对应的函数和其参数列表argsArray的执行行为是否符合预期,返回一个VerificationMode:一个提供验证模式的类,它有times(count)、once()、atLeast(x)、atMost(x)、never()等函数可供选择。 | 329| 16 | times(count) | 验证行为调用过count次。 | 330| 17 | once() | 验证行为调用过一次。 | 331| 18 | atLeast(count) | 验证行为至少调用过count次。 | 332| 19 | atMost(count) | 验证行为至多调用过count次。 | 333| 20 | never | 验证行为从未发生过。 | 334| 21 | ignoreMock(obj, method) | 使用ignoreMock可以还原obj对象中被mock后的函数,对被mock后的函数有效。 | 335| 22 | clearAll() | 用例执行完毕后,进行数据和内存清理。 | 336 337- **使用示例:** 338 339用户可以通过以下方式进行引入mock模块进行测试用例编写: 340 341- **须知:** 342使用时候必须引入的mock能力模块: MockKit,when 343根据自己用例需要引入断言能力api 344例如:`import { describe, expect, it, MockKit, when} from '@ohos/hypium'` 345 346**示例1: afterReturn 的使用** 347 348```javascript 349import { describe, expect, it, MockKit, when } from '@ohos/hypium'; 350 351class ClassName { 352 constructor() { 353 } 354 355 method_1(arg: string) { 356 return '888888'; 357 } 358 359 method_2(arg: string) { 360 return '999999'; 361 } 362} 363export default function afterReturnTest() { 364 describe('afterReturnTest', () => { 365 it('afterReturnTest', 0, () => { 366 console.info("it1 begin"); 367 // 1.创建一个mock能力的对象MockKit 368 let mocker: MockKit = new MockKit(); 369 // 2.定类ClassName,里面两个函数,然后创建一个对象claser 370 let claser: ClassName = new ClassName(); 371 // 3.进行mock操作,比如需要对ClassName类的method_1函数进行mock 372 let mockfunc: Function = mocker.mockFunc(claser, claser.method_1); 373 // 4.期望claser.method_1函数被mock后, 以'test'为入参时调用函数返回结果'1' 374 when(mockfunc)('test').afterReturn('1'); 375 // 5.对mock后的函数进行断言,看是否符合预期 376 // 执行成功案例,参数为'test' 377 expect(claser.method_1('test')).assertEqual('1'); // 执行通过 378 }) 379 }) 380} 381``` 382- **须知:** 383`when(mockfunc)('test').afterReturn('1');` 384这句代码中的`('test')`是mock后的函数需要传递的匹配参数,目前支持一个参数 385`afterReturn('1')`是用户需要预期返回的结果。 386有且只有在参数是`('test')`的时候,执行的结果才是用户自定义的预期结果。 387 388**示例2: afterReturnNothing 的使用** 389 390```javascript 391import { describe, expect, it, MockKit, when } from '@ohos/hypium'; 392 393class ClassName { 394 constructor() { 395 } 396 397 method_1(arg: string) { 398 return '888888'; 399 } 400 401 method_2(arg: string) { 402 return '999999'; 403 } 404} 405export default function afterReturnNothingTest() { 406 describe('afterReturnNothingTest', () => { 407 it('testMockfunc', 0, () => { 408 console.info("it1 begin"); 409 // 1.创建一个mock能力的对象MockKit 410 let mocker: MockKit = new MockKit(); 411 // 2.定类ClassName,里面两个函数,然后创建一个对象claser 412 let claser: ClassName = new ClassName(); 413 // 3.进行mock操作,比如需要对ClassName类的method_1函数进行mock 414 let mockfunc: Function = mocker.mockFunc(claser, claser.method_1); 415 // 4.期望claser.method_1函数被mock后, 以'test'为入参时调用函数返回结果undefined 416 when(mockfunc)('test').afterReturnNothing(); 417 // 5.对mock后的函数进行断言,看是否符合预期,注意选择跟第4步中对应的断言方法 418 // 执行成功案例,参数为'test',这时候执行原对象claser.method_1的方法,会发生变化 419 // 这时候执行的claser.method_1不会再返回'888888',而是设定的afterReturnNothing()生效// 不返回任何值; 420 expect(claser.method_1('test')).assertUndefined(); // 执行通过 421 }) 422 }) 423} 424``` 425 426**示例3: 设定参数类型为any ,即接受任何参数(undefine和null除外)的使用** 427 428 429- **须知:** 430需要引入ArgumentMatchers类,即参数匹配器,例如:ArgumentMatchers.any 431 432```javascript 433import { describe, expect, it, MockKit, when, ArgumentMatchers } from '@ohos/hypium'; 434 435class ClassName { 436 constructor() { 437 } 438 439 method_1(arg: string) { 440 return '888888'; 441 } 442 443 method_2(arg: string) { 444 return '999999'; 445 } 446} 447export default function argumentMatchersAnyTest() { 448 describe('argumentMatchersAnyTest', () => { 449 it('testMockfunc', 0, () => { 450 console.info("it1 begin"); 451 // 1.创建一个mock能力的对象MockKit 452 let mocker: MockKit = new MockKit(); 453 // 2.定类ClassName,里面两个函数,然后创建一个对象claser 454 let claser: ClassName = new ClassName(); 455 // 3.进行mock操作,比如需要对ClassName类的method_1函数进行mock 456 let mockfunc: Function = mocker.mockFunc(claser, claser.method_1); 457 // 4.期望claser.method_1函数被mock后, 以任何参数调用函数时返回结果'1' 458 when(mockfunc)(ArgumentMatchers.any).afterReturn('1'); 459 // 5.对mock后的函数进行断言,看是否符合预期,注意选择跟第4步中对应的断言方法 460 // 执行成功的案例1,传参为字符串类型 461 expect(claser.method_1('test')).assertEqual('1'); // 用例执行通过。 462 // 执行成功的案例2,传参为数字类型123 463 expect(claser.method_1("123")).assertEqual('1');// 用例执行通过。 464 // 执行成功的案例3,传参为boolean类型true 465 expect(claser.method_1("true")).assertEqual('1');// 用例执行通过。 466 }) 467 }) 468} 469``` 470 471**示例4: 设定参数类型ArgumentMatchers的使用** 472 473```javascript 474import { describe, expect, it, MockKit, when, ArgumentMatchers } from '@ohos/hypium'; 475 476class ClassName { 477 constructor() { 478 } 479 480 method_1(arg: string) { 481 return '888888'; 482 } 483 484 method_2(arg: string) { 485 return '999999'; 486 } 487} 488export default function argumentMatchersTest() { 489 describe('argumentMatchersTest', () => { 490 it('testMockfunc', 0, () => { 491 console.info("it1 begin"); 492 // 1.创建一个mock能力的对象MockKit 493 let mocker: MockKit = new MockKit(); 494 // 2.定类ClassName,里面两个函数,然后创建一个对象claser 495 let claser: ClassName = new ClassName(); 496 // 3.进行mock操作,比如需要对ClassName类的method_1函数进行mock 497 let mockfunc: Function = mocker.mockFunc(claser, claser.method_1); 498 // 4.期望claser.method_1函数被mock后, 以任何string类型为参数调用函数时返回结果'1' 499 when(mockfunc)(ArgumentMatchers.anyString).afterReturn('1'); 500 // 4.对mock后的函数进行断言,看是否符合预期,注意选择跟第4步中对应的断言方法 501 // 执行成功的案例,传参为字符串类型 502 expect(claser.method_1('test')).assertEqual('1'); // 用例执行通过。 503 expect(claser.method_1('abc')).assertEqual('1'); // 用例执行通过。 504 }) 505 }) 506} 507``` 508 509**示例5: 设定参数类型为matchRegexs(Regex)等 的使用** 510```javascript 511import { describe, expect, it, MockKit, when, ArgumentMatchers } from '@ohos/hypium'; 512 513class ClassName { 514 constructor() { 515 } 516 517 method_1(arg: string) { 518 return '888888'; 519 } 520 521 method_2(arg: string) { 522 return '999999'; 523 } 524} 525export default function matchRegexsTest() { 526 describe('matchRegexsTest', () => { 527 it('testMockfunc', 0, () => { 528 console.info("it1 begin"); 529 // 1.创建一个mock能力的对象MockKit 530 let mocker: MockKit = new MockKit(); 531 let claser: ClassName = new ClassName(); 532 // 2.进行mock操作,比如需要对ClassName类的method_1函数进行mock 533 let mockfunc: Function = mocker.mockFunc(claser, claser.method_1); 534 // 3.期望claser.method_1函数被mock后, 以"test"为入参调用函数时返回结果'1' 535 when(mockfunc)(ArgumentMatchers.matchRegexs(new RegExp("test"))).afterReturn('1'); 536 // 4.对mock后的函数进行断言,看是否符合预期,注意选择跟第4步中对应的断言方法 537 // 执行成功的案例,传参为字符串类型 538 expect(claser.method_1('test')).assertEqual('1'); // 用例执行通过。 539 }) 540 }) 541} 542``` 543 544**示例6: 验证功能 Verify函数的使用** 545```javascript 546import { describe, it, MockKit } from '@ohos/hypium'; 547 548class ClassName { 549 constructor() { 550 } 551 552 method_1(...arg: string[]) { 553 return '888888'; 554 } 555 556 method_2(...arg: string[]) { 557 return '999999'; 558 } 559} 560export default function verifyTest() { 561 describe('verifyTest', () => { 562 it('testMockfunc', 0, () => { 563 console.info("it1 begin"); 564 // 1.创建一个mock能力的对象MockKit 565 let mocker: MockKit = new MockKit(); 566 // 2.然后创建一个对象claser 567 let claser: ClassName = new ClassName(); 568 // 3.进行mock操作,比如需要对ClassName类的method_1和method_2两个函数进行mock 569 mocker.mockFunc(claser, claser.method_1); 570 mocker.mockFunc(claser, claser.method_2); 571 // 4.方法调用如下 572 claser.method_1('abc', 'ppp'); 573 claser.method_1('abc'); 574 claser.method_1('xyz'); 575 claser.method_1(); 576 claser.method_1('abc', 'xxx', 'yyy'); 577 claser.method_1(); 578 claser.method_2('111'); 579 claser.method_2('111', '222'); 580 // 5.现在对mock后的两个函数进行验证,验证method_2,参数为'111'执行过一次 581 mocker.verify('method_2',['111']).once(); // 执行success 582 }) 583 }) 584} 585``` 586 587**示例7: ignoreMock(obj, method) 忽略函数的使用** 588```javascript 589import { describe, expect, it, MockKit, when, ArgumentMatchers } from '@ohos/hypium'; 590 591class ClassName { 592 constructor() { 593 } 594 595 method_1(...arg: number[]) { 596 return '888888'; 597 } 598 599 method_2(...arg: number[]) { 600 return '999999'; 601 } 602} 603export default function ignoreMockTest() { 604 describe('ignoreMockTest', () => { 605 it('testMockfunc', 0, () => { 606 console.info("it1 begin"); 607 // 1.创建一个mock能力的对象MockKit 608 let mocker: MockKit = new MockKit(); 609 // 2.创建一个对象claser 610 let claser: ClassName = new ClassName(); 611 // 3.进行mock操作,比如需要对ClassName类的method_1和method_2两个函数进行mock 612 let func_1: Function = mocker.mockFunc(claser, claser.method_1); 613 let func_2: Function = mocker.mockFunc(claser, claser.method_2); 614 // 4.期望claser.method_1函数被mock后, 以number类型为入参时调用函数返回结果'4' 615 when(func_1)(ArgumentMatchers.anyNumber).afterReturn('4'); 616 // 4.期望claser.method_2函数被mock后, 以number类型为入参时调用函数返回结果'5' 617 when(func_2)(ArgumentMatchers.anyNumber).afterReturn('5'); 618 // 5.方法调用如下 619 expect(claser.method_1(123)).assertEqual("4"); 620 expect(claser.method_2(456)).assertEqual("5"); 621 // 6.现在对mock后的两个函数的其中一个函数method_1进行忽略处理(原理是就是还原) 622 mocker.ignoreMock(claser, claser.method_1); 623 // 7.然后再去调用 claser.method_1函数,用断言测试結果 624 expect(claser.method_1(123)).assertEqual('888888'); 625 }) 626 }) 627} 628``` 629 630**示例8: clear(obj)函数的使用** 631 632```javascript 633import { describe, expect, it, MockKit, when, ArgumentMatchers } from '@ohos/hypium'; 634 635class ClassName { 636 constructor() { 637 } 638 639 method_1(...arg: number[]) { 640 return '888888'; 641 } 642 643 method_2(...arg: number[]) { 644 return '999999'; 645 } 646} 647export default function clearTest() { 648 describe('clearTest', () => { 649 it('testMockfunc', 0, () => { 650 console.info("it1 begin"); 651 // 1.创建一个mock能力的对象MockKit 652 let mocker: MockKit = new MockKit(); 653 // 2.创建一个对象claser 654 let claser: ClassName = new ClassName(); 655 // 3.进行mock操作,比如需要对ClassName类的method_1和method_2两个函数进行mock 656 let func_1: Function = mocker.mockFunc(claser, claser.method_1); 657 let func_2: Function = mocker.mockFunc(claser, claser.method_2); 658 // 4.期望claser.method_1函数被mock后, 以任何number类型为参数调用函数时返回结果'4' 659 when(func_1)(ArgumentMatchers.anyNumber).afterReturn('4'); 660 // 4.期望claser.method_2函数被mock后, 以任何number类型为参数调用函数时返回结果'5' 661 when(func_2)(ArgumentMatchers.anyNumber).afterReturn('5'); 662 // 5.方法调用如下 663 expect(claser.method_1(123)).assertEqual('4'); 664 expect(claser.method_2(123)).assertEqual('5'); 665 // 6.清除obj上所有的mock能力(原理是就是还原) 666 mocker.clear(claser); 667 // 7.然后再去调用 claser.method_1,claser.method_2 函数,测试结果 668 expect(claser.method_1(123)).assertEqual('888888'); 669 expect(claser.method_2(123)).assertEqual('999999'); 670 }) 671 }) 672} 673``` 674 675**示例9: afterThrow(msg)函数的使用** 676 677```javascript 678import { describe, expect, it, MockKit, when } from '@ohos/hypium'; 679 680class ClassName { 681 constructor() { 682 } 683 684 method_1(arg: string) { 685 return '888888'; 686 } 687} 688export default function afterThrowTest() { 689 describe('afterThrowTest', () => { 690 it('testMockfunc', 0, () => { 691 console.info("it1 begin"); 692 // 1.创建一个mock能力的对象MockKit 693 let mocker: MockKit = new MockKit(); 694 // 2.创建一个对象claser 695 let claser: ClassName = new ClassName(); 696 // 3.进行mock操作,比如需要对ClassName类的method_1函数进行mock 697 let mockfunc: Function = mocker.mockFunc(claser, claser.method_1); 698 // 4.期望claser.method_1函数被mock后, 以'test'为参数调用函数时抛出error xxx异常 699 when(mockfunc)('test').afterThrow('error xxx'); 700 // 5.执行mock后的函数,捕捉异常并使用assertEqual对比msg否符合预期 701 try { 702 claser.method_1('test'); 703 } catch (e) { 704 expect(e).assertEqual('error xxx'); // 执行通过 705 } 706 }) 707 }) 708} 709``` 710 711**示例10: mock异步返回promise对象的使用** 712 713```javascript 714import { describe, expect, it, MockKit, when } from '@ohos/hypium'; 715 716class ClassName { 717 constructor() { 718 } 719 720 async method_1(arg: string) { 721 return new Promise<string>((resolve: Function, reject: Function) => { 722 setTimeout(() => { 723 console.log('执行'); 724 resolve('数据传递'); 725 }, 2000); 726 }); 727 } 728} 729export default function mockPromiseTest() { 730 describe('mockPromiseTest', () => { 731 it('testMockfunc', 0, async (done: Function) => { 732 console.info("it1 begin"); 733 // 1.创建一个mock能力的对象MockKit 734 let mocker: MockKit = new MockKit(); 735 // 2.创建一个对象claser 736 let claser: ClassName = new ClassName(); 737 // 3.进行mock操作,比如需要对ClassName类的method_1函数进行mock 738 let mockfunc: Function = mocker.mockFunc(claser, claser.method_1); 739 // 4.期望claser.method_1函数被mock后, 以'test'为参数调用函数时返回一个promise对象 740 when(mockfunc)('test').afterReturn(new Promise<string>((resolve: Function, reject: Function) => { 741 console.log("do something"); 742 resolve('success something'); 743 })); 744 // 5.执行mock后的函数,即对定义的promise进行后续执行 745 let result = await claser.method_1('test'); 746 expect(result).assertEqual("success something"); 747 done(); 748 }) 749 }) 750} 751``` 752 753**示例11:verify times函数的使用(验证函数调用次数)** 754 755```javascript 756import { describe, it, MockKit, when } from '@ohos/hypium' 757 758class ClassName { 759 constructor() { 760 } 761 762 method_1(...arg: string[]) { 763 return '888888'; 764 } 765} 766export default function verifyTimesTest() { 767 describe('verifyTimesTest', () => { 768 it('test_verify_times', 0, () => { 769 // 1.创建MockKit对象 770 let mocker: MockKit = new MockKit(); 771 // 2.创建类对象 772 let claser: ClassName = new ClassName(); 773 // 3.mock 类ClassName对象的某个方法,比如method_1 774 let func_1: Function = mocker.mockFunc(claser, claser.method_1); 775 // 4.期望被mock后的函数返回结果'4' 776 when(func_1)('123').afterReturn('4'); 777 // 5.随机执行几次函数,参数如下 778 claser.method_1('123', 'ppp'); 779 claser.method_1('abc'); 780 claser.method_1('xyz'); 781 claser.method_1(); 782 claser.method_1('abc', 'xxx', 'yyy'); 783 claser.method_1('abc'); 784 claser.method_1(); 785 // 6.验证函数method_1且参数为'abc'时,执行过的次数是否为2 786 mocker.verify('method_1', ['abc']).times(2); 787 }) 788 }) 789} 790``` 791 792 793**示例12:verify atLeast函数的使用(验证函数调用次数)** 794 795```javascript 796import { describe, it, MockKit, when } from '@ohos/hypium' 797 798class ClassName { 799 constructor() { 800 } 801 802 method_1(...arg: string[]) { 803 return '888888'; 804 } 805} 806export default function verifyAtLeastTest() { 807 describe('verifyAtLeastTest', () => { 808 it('test_verify_atLeast', 0, () => { 809 // 1.创建MockKit对象 810 let mocker: MockKit = new MockKit(); 811 // 2.创建类对象 812 let claser: ClassName = new ClassName(); 813 // 3.mock 类ClassName对象的某个方法,比如method_1 814 let func_1: Function = mocker.mockFunc(claser, claser.method_1); 815 // 4.期望被mock后的函数返回结果'4' 816 when(func_1)('123').afterReturn('4'); 817 // 5.随机执行几次函数,参数如下 818 claser.method_1('123', 'ppp'); 819 claser.method_1('abc'); 820 claser.method_1('xyz'); 821 claser.method_1(); 822 claser.method_1('abc', 'xxx', 'yyy'); 823 claser.method_1(); 824 // 6.验证函数method_1且参数为空时,是否至少执行过2次 825 mocker.verify('method_1', []).atLeast(2); 826 }) 827 }) 828} 829``` 830 831#### 数据驱动 832 833##### 约束限制 834 835单元测试框架数据驱动能力从[框架 1.0.2版本](https://ohpm.openharmony.cn/#/cn/detail/@ohos%2Fhypium)开始支持。 836 837- 数据参数传递 : 为指定测试套、测试用例传递测试输入数据参数。 838- 压力测试 : 为指定测试套、测试用例设置执行次数。 839 840数据驱动可以根据配置参数来驱动测试用例的执行次数和每一次传入的参数,使用时依赖data.json配置文件,文件内容如下: 841 842>说明 : data.json与测试用例*.test.js|ets文件同目录 843 844```json 845{ 846 "suites": [{ 847 "describe": ["actsAbilityTest"], 848 "stress": 2, 849 "params": { 850 "suiteParams1": "suiteParams001", 851 "suiteParams2": "suiteParams002" 852 }, 853 "items": [{ 854 "it": "testDataDriverAsync", 855 "stress": 2, 856 "params": [{ 857 "name": "tom", 858 "value": 5 859 }, { 860 "name": "jerry", 861 "value": 4 862 }] 863 }, { 864 "it": "testDataDriver", 865 "stress": 3 866 }] 867 }] 868} 869``` 870 871配置参数说明: 872 873| | 配置项名称 | 功能 | 必填 | 874| :--- | :--------- | :------------------------------------ | ---- | 875| 1 | "suite" | 测试套配置 。 | 是 | 876| 2 | "items" | 测试用例配置 。 | 是 | 877| 3 | "describe" | 测试套名称 。 | 是 | 878| 4 | "it" | 测试用例名称 。 | 是 | 879| 5 | "params" | 测试套 / 测试用例 可传入使用的参数 。 | 否 | 880| 6 | "stress" | 测试套 / 测试用例 指定执行次数 。 | 否 | 881 882示例代码: 883 884DevEco Studio从V3.0 Release(2022-09-06)版本开始支持 885 886stage模型: 887 888在TestAbility目录下TestAbility.ets文件中导入data.json,并在Hypium.hypiumTest() 方法执行前,设置参数数据 889 890FA模型: 891 892在TestAbility目录下app.js或app.ets文件中导入data.json,并在Hypium.hypiumTest() 方法执行前,设置参数数据 893 894```javascript 895import AbilityDelegatorRegistry from '@ohos.application.abilityDelegatorRegistry' 896import { Hypium } from '@ohos/hypium' 897import testsuite from '../test/List.test' 898import data from '../test/data.json'; 899 900... 901Hypium.setData(data); 902Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite); 903... 904``` 905 906```javascript 907 import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; 908 909 export default function abilityTest() { 910 describe('actsAbilityTest', () => { 911 it('testDataDriverAsync', 0, async (done: Function, data: ParmObj) => { 912 console.info('name: ' + data.name); 913 console.info('value: ' + data.value); 914 done(); 915 }); 916 917 it('testDataDriver', 0, () => { 918 console.info('stress test'); 919 }) 920 }) 921} 922 interface ParmObj { 923 name: string, 924 value: string 925 } 926``` 927#### 专项能力 928 929该项能力需通过在cmd窗口中输入aa test命令执行触发,并通过设置执行参数触发不同功能。另外,测试应用模型与编译方式不同,对应的aa test命令也不同,具体可参考[自动化测试框架使用指导](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/application-test/arkxtest-guidelines.md#cmd%E6%89%A7%E8%A1%8C) 930 931- **筛选能力** 932 933 1、按测试用例属性筛选 934 935 可以利用框架提供的Level、Size、TestType 对象,对测试用例进行标记,以区分测试用例的级别、粒度、测试类型,各字段含义及代码如下: 936 937 | Key | 含义说明 | Value取值范围 | 938 | -------- | ------------ | ------------------------------------------------------------ | 939 | level | 用例级别 | "0","1","2","3","4", 例如:-s level 1 | 940 | size | 用例粒度 | "small","medium","large", 例如:-s size small | 941 | testType | 用例测试类型 | "function","performance","power","reliability","security","global","compatibility","user","standard","safety","resilience", 例如:-s testType function | 942 943 示例代码: 944 945 ```javascript 946 import { describe, it, expect, TestType, Size, Level } from '@ohos/hypium'; 947 948 export default function attributeTest() { 949 describe('attributeTest', () => { 950 it("testAttributeIt", TestType.FUNCTION | Size.SMALLTEST | Level.LEVEL0, () => { 951 console.info('Hello Test'); 952 }) 953 }) 954} 955 ``` 956 957 示例命令: 958 959 ```shell 960 hdc shell aa test -b xxx -m xxx -s unittest OpenHarmonyTestRunner -s testType function -s size small -s level 0 961 ``` 962 963 该命令作用是筛选测试应用中同时满足,用例测试类型是“function”、用例粒度是“small”、用例级别是“0”的三个条件用例执行。 964 965 2、按测试套/测试用例名称筛选 966 967 注意:测试套和测试用例的命名要符合框架规则,即以字母开头,后跟一个或多个字母、数字,不能包含特殊符号。 968 969 框架可以通过指定测试套与测试用例名称,来指定特定用例的执行,测试套与用例名称用“#”号连接,多个用“,”英文逗号分隔 970 971 | Key | 含义说明 | Value取值范围 | 972 | -------- | ----------------------- | ------------------------------------------------------------ | 973 | class | 指定要执行的测试套&用例 | ${describeName}#${itName},${describeName} , 例如:-s class attributeTest#testAttributeIt | 974 | notClass | 指定不执行的测试套&用例 | ${describeName}#${itName},${describeName} , 例如:-s notClass attributeTest#testAttribut | 975 976 示例代码: 977 978 ```javascript 979 import { describe, it, expect, TestType, Size, Level } from '@ohos/hypium'; 980 981 export default function attributeTest() { 982 describe('describeTest_000', () => { 983 it("testIt_00", TestType.FUNCTION | Size.SMALLTEST | Level.LEVEL0, () => { 984 console.info('Hello Test'); 985 }) 986 987 it("testIt_01", TestType.FUNCTION | Size.SMALLTEST | Level.LEVEL0, () => { 988 console.info('Hello Test'); 989 }) 990 }) 991 992 describe('describeTest_001', () => { 993 it("testIt_02", TestType.FUNCTION | Size.SMALLTEST | Level.LEVEL0, () => { 994 console.info('Hello Test'); 995 }) 996 }) 997} 998 ``` 999 1000 示例命令1: 1001 1002 ```shell 1003 hdc shell aa test -b xxx -m xxx -s unittest OpenHarmonyTestRunner -s class describeTest_000#testIt_00,describeTest_001 1004 ``` 1005 1006 该命令作用是执行“describeTest_001”测试套中所用用例,以及“describeTest_000”测试套中的“testIt_00”用例。 1007 1008 示例命令2: 1009 1010 ```shell 1011 hdc shell aa test -b xxx -m xxx -s unittest OpenHarmonyTestRunner -s notClass describeTest_000#testIt_01 1012 ``` 1013 1014 该命令作用是不执行“describeTest_000”测试套中的“testIt_01”用例。 1015 1016- **随机执行** 1017 1018 使测试套与测试用例随机执行,用于稳定性测试。 1019 1020 | Key | 含义说明 | Value取值范围 | 1021 | ------ | ------------------------------------ | ---------------------------------------------- | 1022 | random | @since1.0.3 测试套、测试用例随机执行 | true, 不传参默认为false, 例如:-s random true | 1023 1024 示例命令: 1025 1026 ```shell 1027 hdc shell aa test -b xxx -m xxx -s unittest OpenHarmonyTestRunner -s random true 1028 ``` 1029 1030- **压力测试** 1031 1032 指定要执行用例的执行次数,用于压力测试。 1033 1034 | Key | 含义说明 | Value取值范围 | 1035 | ------ | ------------------------------------ | ------------------------------ | 1036 | stress | @since1.0.5 指定要执行用例的执行次数 | 正整数, 例如: -s stress 1000 | 1037 1038 示例命令: 1039 1040 ```shell 1041 hdc shell aa test -b xxx -m xxx -s unittest OpenHarmonyTestRunner -s stress 1000 1042 ``` 1043 1044- **用例超时时间设置** 1045 1046 指定测试用例执行的超时时间,用例实际耗时如果大于超时时间,用例会抛出"timeout"异常,用例结果会显示“excute timeout XXX” 1047 1048 | Key | 含义说明 | Value取值范围 | 1049 | ------- | -------------------------- | ---------------------------------------------------- | 1050 | timeout | 指定测试用例执行的超时时间 | 正整数(单位ms),默认为 5000,例如: -s timeout 15000 | 1051 1052 示例命令: 1053 1054 ```shell 1055 hdc shell aa test -b xxx -m xxx -s unittest OpenHarmonyTestRunner -s timeout 15000 1056 ``` 1057 1058- **遇错即停模式** 1059 1060 | Key | 含义说明 | Value取值范围 | 1061 | ------------ | ------------------------------------------------------------ | ---------------------------------------------------- | 1062 | breakOnError | @since1.0.6 遇错即停模式,当执行用例断言失败或者发生错误时,退出测试执行流程 | true, 不传参默认为false, 例如:-s breakOnError true | 1063 1064 示例命令: 1065 1066 ```shell 1067 hdc shell aa test -b xxx -m xxx -s unittest OpenHarmonyTestRunner -s breakOnError true 1068 ``` 1069 1070- **测试套中用例信息输出** 1071 1072 输出测试应用中待执行的测试用例信息 1073 1074 | Key | 含义说明 | Value取值范围 | 1075 | ------ | ---------------------------- | ---------------------------------------------- | 1076 | dryRun | 显示待执行的测试用例信息全集 | true, 不传参默认为false, 例如:-s dryRun true | 1077 1078 示例命令: 1079 1080 ```shell 1081 hdc shell aa test -b xxx -m xxx -s unittest OpenHarmonyTestRunner -s dryRun true 1082 ``` 1083 1084- **嵌套能力** 1085 1086 1.示例代码 1087 ```javascript 1088 // Test1.test.ets 1089 import { describe, expect, it } from '@ohos/hypium'; 1090 import test2 from './Test2.test'; 1091 1092 export default function test1() { 1093 describe('test1', () => { 1094 it('assertContain1', 0, () => { 1095 let a = true; 1096 let b = true; 1097 expect(a).assertEqual(b); 1098 }) 1099 // 引入测试套test2 1100 test2(); 1101 }) 1102 } 1103 ``` 1104 1105 ```javascript 1106 //Test2.test.ets 1107 import { describe, expect, it } from '@ohos/hypium'; 1108 1109 export default function test2() { 1110 describe('test2', () => { 1111 it('assertContain1', 0, () => { 1112 let a = true; 1113 let b = true; 1114 expect(a).assertEqual(b); 1115 }) 1116 it('assertContain2', 0, () => { 1117 let a = true; 1118 let b = true; 1119 expect(a).assertEqual(b); 1120 }) 1121 }) 1122 } 1123 ``` 1124 1125 ```javascript 1126 //List.test.ets 1127 import test1 from './nest/Test1.test'; 1128 1129 export default function testsuite() { 1130 test1(); 1131 } 1132 ``` 1133 1134 2.示例筛选参数 1135 ```shell 1136 #执行test1的全部测试用例 1137 -s class test1 1138 ``` 1139 ```shell 1140 #执行test1的子测试用例 1141 -s class test1#assertContain1 1142 ``` 1143 ```shell 1144 #执行test1的子测试套test2的测试用例 1145 -s class test1.test2#assertContain1 1146 ``` 1147 1148- **跳过能力** 1149 1150 | Key | 含义说明 | Value取值范围 | 1151 | ------------ | ------------------------------------------------------------ | ---------------------------------------------------- | 1152 | skipMessage | @since1.0.17 显示待执行的测试用例信息全集中是否包含跳过测试套和跳过用例的信息 | true/false, 不传参默认为false, 例如:-s skipMessage true | 1153 | runSkipped | @since1.0.17 指定要执行的跳过测试套&跳过用例 | all,skipped,${describeName}#${itName},${describeName},不传参默认为空,例如:-s runSkipped all | 1154 1155 1.示例代码 1156 1157 ```javascript 1158 //Skip1.test.ets 1159 import { expect, xdescribe, xit } from '@ohos/hypium'; 1160 1161 export default function skip1() { 1162 xdescribe('skip1', () => { 1163 //注意:在xdescribe中不支持编写it用例 1164 xit('assertContain1', 0, () => { 1165 let a = true; 1166 let b = true; 1167 expect(a).assertEqual(b); 1168 }) 1169 }) 1170 } 1171 ``` 1172 1173 ```javascript 1174 //Skip2.test.ets 1175 import { describe, expect, xit, it } from '@ohos/hypium'; 1176 1177 export default function skip2() { 1178 describe('skip2', () => { 1179 //默认会跳过assertContain1 1180 xit('assertContain1', 0, () => { 1181 let a = true; 1182 let b = true; 1183 expect(a).assertEqual(b); 1184 }) 1185 it('assertContain2', 0, () => { 1186 let a = true; 1187 let b = true; 1188 expect(a).assertEqual(b); 1189 }) 1190 }) 1191 } 1192 ``` 1193 1194 1195 1196### 使用方式 1197 1198单元测试框架以ohpm包形式发布至[服务组件官网](https://ohpm.openharmony.cn/#/cn/detail/@ohos%2Fhypium),开发者可以下载Deveco Studio后,在应用工程中配置依赖后使用框架能力,测试工程创建及测试脚本执行使用指南请参见[IDE指导文档](https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ohos-openharmony-test-framework-0000001263160453)。 1199 1200## Ui测试框架功能特性 1201 1202| No. | 特性 | 功能说明 | 1203| ---- |-----------|-------------------------------------------------| 1204| 1 | Driver | Ui测试的入口,提供查找控件,检查控件存在性以及注入按键能力。 | 1205| 2 | On | 用于描述目标控件特征(文本、id、类型等),`Driver`根据`On`描述的控件特征信息来查找控件。 | 1206| 3 | Component | Driver查找返回的控件对象,提供查询控件属性,滑动查找等触控和检视能力。 | 1207| 4 | UiWindow | Driver查找返回的窗口对象,提供获取窗口属性、操作窗口的能力。 | 1208 1209**使用者在测试脚本通过如下方式引入使用:** 1210 1211```typescript 1212import {Driver,ON,Component,UiWindow,MatchPattern} from '@ohos.UiTest' 1213``` 1214 1215> 须知 1216> 1. `On`类提供的接口全部是同步接口,使用者可以使用`builder`模式链式调用其接口构造控件筛选条件。 1217> 2. `Driver`和`Component`类提供的接口全部是异步接口(`Promise`形式),**需使用`await`语法**。 1218> 3. Ui测试用例均需使用**异步**语法编写用例,需遵循单元测试框架异步用例编写规范。 1219 1220 1221 1222在测试用例文件中import `On/Driver/Component`类,然后调用API接口编写测试用例。 1223 1224```javascript 1225import { Driver, ON, Component } from '@kit.TestKit' 1226import { describe, it, expect } from '@ohos/hypium' 1227 1228export default function findComponentTest() { 1229 describe('findComponentTest', () => { 1230 it('uitest_demo0', 0, async () => { 1231 // create Driver 1232 let driver: Driver = Driver.create(); 1233 // find component by text 1234 let button: Component = await driver.findComponent(ON.text('Hello World').enabled(true)); 1235 // click component 1236 await button.click(); 1237 // get and assert component text 1238 let content: string = await button.getText(); 1239 expect(content).assertEqual('Hello World'); 1240 }) 1241 }) 1242} 1243``` 1244 1245### Driver使用说明 1246 1247`Driver`类作为UiTest测试框架的总入口,提供查找控件,注入按键,单击坐标,滑动控件,手势操作,截图等能力。 1248 1249| No. | API | 功能描述 | 1250| ---- |-----------------------------------------------------------------| ---------------------- | 1251| 1 | create():Promise<Driver> | 静态方法,构造Driver。 | 1252| 2 | findComponent(on:On):Promise<Component> | 查找匹配控件。 | 1253| 3 | pressBack():Promise<void> | 单击BACK键。 | 1254| 4 | click(x:number, y:number):Promise<void> | 基于坐标点的单击。 | 1255| 5 | swipe(x1:number, y1:number, x2:number, y2:number):Promise<void> | 基于坐标点的滑动。 | 1256| 6 | assertComponentExist(on:On):Promise<void> | 断言匹配的控件存在。 | 1257| 7 | delayMs(t:number):Promise<void> | 延时。 | 1258| 8 | screenCap(s:path):Promise<void> | 截屏。 | 1259| 9 | findWindow(filter: WindowFilter): Promise<UiWindow> | 查找匹配窗口。 | 1260 1261其中assertComponentExist接口是断言API,用于断言当前界面存在目标控件;如果控件不存在,该API将抛出JS异常,使当前测试用例失败。 1262 1263```javascript 1264import { describe, it} from '@ohos/hypium'; 1265import { Driver, ON } from '@kit.TestKit'; 1266 1267export default function assertComponentExistTest() { 1268 describe('assertComponentExistTest', () => { 1269 it('Uitest_demo0', 0, async (done: Function) => { 1270 try{ 1271 // create Driver 1272 let driver: Driver = Driver.create(); 1273 // assert text 'hello' exists on current Ui 1274 await driver.assertComponentExist(ON.text('hello')); 1275 } finally { 1276 done(); 1277 } 1278 }) 1279 }) 1280} 1281``` 1282 1283`Driver`完整的API列表请参考[API文档](https://gitee.com/openharmony/interface_sdk-js/blob/master/api/@ohos.UiTest.d.ts)及[示例文档说明](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis-test-kit/js-apis-uitest.md#driver9)。 1284 1285### On使用说明 1286 1287Ui测试框架通过`On`类提供了丰富的控件特征描述API,用来匹配查找要操作或检视的目标控件。`On`提供的API能力具有以下特点: 1288 1289- 支持匹配单属性和匹配多属性组合,例如同时指定目标控件text和id。 1290- 控件属性支持多种匹配模式(等于,包含,`STARTS_WITH`,`ENDS_WITH`)。 1291- 支持相对定位控件,可通过`isBefore`和`isAfter`等API限定邻近控件特征进行辅助定位。 1292 1293| No. | API | 功能描述 | 1294|-----|------------------------------------|----------------------------| 1295| 1 | id(i:string):On | 指定控件id。 | 1296| 2 | text(t:string, p?:MatchPattern):On | 指定控件文本,可指定匹配模式。 | 1297| 3 | type(t:string):On | 指定控件类型。 | 1298| 4 | enabled(e:bool):On | 指定控件使能状态。 | 1299| 5 | clickable(c:bool):On | 指定控件可单击状态。 | 1300| 6 | longClickable(l:bool):On | 指定控件可长按状态。 | 1301| 7 | focused(f:bool):On | 指定控件获焦状态。 | 1302| 8 | scrollable(s:bool):On | 指定控件可滑动状态。 | 1303| 9 | selected(s:bool):On | 指定控件选中状态。 | 1304| 10 | checked(c:bool):On | 指定控件选择状态。 | 1305| 11 | checkable(c:bool):On | 指定控件可选择状态。 | 1306| 12 | isBefore(b:On):On | **相对定位**,限定目标控件位于指定特征控件之前。 | 1307| 13 | isAfter(b:On):On | **相对定位**,限定目标控件位于指定特征控件之后。 | 1308 1309其中,`text`属性支持{`MatchPattern.EQUALS`,`MatchPattern.CONTAINS`,`MatchPattern.STARTS_WITH`,`MatchPattern.ENDS_WITH`}四种匹配模式,缺省使用`MatchPattern.EQUALS`模式。 1310 1311`On`完整的API列表请参考[API文档](https://gitee.com/openharmony/interface_sdk-js/blob/master/api/@ohos.UiTest.d.ts)及[示例文档说明](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis-test-kit/js-apis-uitest.md#on9)。 1312 1313#### 控件绝对定位 1314 1315**示例代码1**:查找id是`Id_button`的控件。 1316 1317```javascript 1318let button: Component = await driver.findComponent(ON.id('Id_button')) 1319``` 1320 1321 **示例代码2**:查找id是`Id_button`并且状态是`enabled`的控件,适用于无法通过单一属性定位的场景。 1322 1323```javascript 1324let button: Component = await driver.findComponent(ON.id('Id_button').enabled(true)) 1325``` 1326 1327通过`On.id(x).enabled(y)`来指定目标控件的多个属性。 1328 1329**示例代码3**:查找文本中包含`hello`的控件,适用于不能完全确定控件属性取值的场景。 1330 1331```javascript 1332let txt: Component = await driver.findComponent(ON.text('hello', MatchPattern.CONTAINS)) 1333``` 1334 1335通过向`On.text()`方法传入第二个参数`MatchPattern.CONTAINS`来指定文本匹配规则;默认规则是`MatchPattern.EQUALS`,即目标控件text属性必须严格等于给定值。 1336 1337#### 控件相对定位 1338 1339**示例代码1**:查找位于文本控件`Item3_3`后面的,id是`Id_switch`的Switch控件。 1340 1341```javascript 1342let switch: Component = await driver.findComponent(ON.id('Id_switch').isAfter(ON.text('Item3_3'))) 1343``` 1344 1345通过`On.isAfter`方法,指定位于目标控件前面的特征控件属性,通过该特征控件进行相对定位。一般地,特征控件是某个具有全局唯一特征的控件(例如具有唯一的id或者唯一的text)。 1346 1347类似的,可以使用`On.isBefore`控件指定位于目标控件后面的特征控件属性,实现相对定位。 1348 1349### Component使用说明 1350 1351`Component`类代表了Ui界面上的一个控件,一般是通过`Driver.findComponent(on)`方法查找到的。通过该类的实例,用户可以获取控件属性,单击控件,滑动查找,注入文本等操作。 1352 1353`Component`包含的常用API: 1354 1355| No. | API | 功能描述 | 1356|-----|------------------------------------|----------------------------| 1357| 1 | click():Promise<void> | 单击该控件。 | 1358| 2 | inputText(t:string):Promise<void> | 向控件中输入文本(适用于文本框控件)。 | 1359| 3 | scrollSearch(s:On):Promise<Component> | 在该控件上滑动查找目标控件(适用于List等控件)。 | 1360| 4 | scrollToTop(s:number):Promise<void> | 滑动到该控件顶部(适用于List等控件)。 | 1361| 5 | scrollTobottom(s:number):Promise<void> | 滑动到该控件底部(适用于List等控件)。 | 1362| 6 | getText():Promise<string> | 获取控件text。 | 1363| 7 | getId():Promise<number> | 获取控件id。 | 1364| 8 | getType():Promise<string> | 获取控件类型。 | 1365| 9 | isEnabled():Promise<bool> | 获取控件使能状态。 | 1366 1367`Component`完整的API列表请参考[API文档](https://gitee.com/openharmony/interface_sdk-js/blob/master/api/@ohos.UiTest.d.ts)及[示例文档说明](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis-test-kit/js-apis-uitest.md#component9)。 1368 1369**示例代码1**:单击控件。 1370 1371```javascript 1372let button: Component = await driver.findComponent(ON.id('Id_button')) 1373await button.click() 1374``` 1375 1376**示例代码2**:通过get接口获取控件属性后,可以使用单元测试框架提供的assert*接口做断言检查。 1377 1378```javascript 1379let component: Component = await driver.findComponent(ON.id('Id_title')) 1380expect(component !== null).assertTrue() 1381``` 1382 1383**示例代码3**:在List控件中滑动查找text是`Item3_3`的子控件。 1384 1385```javascript 1386let list: Component = await driver.findComponent(ON.id('Id_list')) 1387let found: Component = await list.scrollSearch(ON.text('Item3_3')) 1388expect(found).assertTrue() 1389``` 1390 1391**示例代码4**:向输入框控件中输入文本。 1392 1393```javascript 1394let editText: Component = await driver.findComponent(ON.type('InputText')) 1395await editText.inputText('user_name') 1396``` 1397### UiWindow使用说明 1398 1399`UiWindow`类代表了Ui界面上的一个窗口,一般是通过`Driver.findWindow(WindowFilter)`方法查找到的。通过该类的实例,用户可以获取窗口属性,并进行窗口拖动、调整窗口大小等操作。 1400 1401`UiWindow`包含的常用API: 1402 1403| No. | API | 功能描述 | 1404| ---- | ------------------------------------------------------------ | -------------------------------------------------- | 1405| 1 | getBundleName(): Promise<string> | 获取窗口所属应用包名。 | 1406| 2 | getTitle(): Promise<string> | 获取窗口标题信息。 | 1407| 3 | focus(): Promise<bool> | 使得当前窗口获取焦点。 | 1408| 4 | moveTo(x: number, y: number): Promise<bool> | 将当前窗口移动到指定位置(适用于支持移动的窗口)。 | 1409| 5 | resize(wide: number, height: number, direction: ResizeDirection): Promise<bool> | 调整窗口大小(适用于支持调整大小的窗口)。 | 1410| 6 | split(): Promise<bool> | 将窗口模式切换为分屏模式(适用于支持分屏的窗口)。 | 1411| 7 | close(): Promise<bool> | 关闭当前窗口。 | 1412 1413`UiWindow`完整的API列表请参考[API文档](https://gitee.com/openharmony/interface_sdk-js/blob/master/api/@ohos.UiTest.d.ts)及[示例文档说明](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis-test-kit/js-apis-uitest.md#uiwindow9)。 1414 1415**示例代码1**:获取窗口属性。 1416 1417```javascript 1418let window: UiWindow = await driver.findWindow({actived: true}) 1419let bundelName: string = await window.getBundleName() 1420``` 1421 1422**示例代码2**:移动窗口。 1423 1424```javascript 1425let window: UiWindow = await driver.findWindow({actived: true}) 1426await window.moveTo(500,500) 1427``` 1428 1429**示例代码3**:关闭窗口。 1430 1431```javascript 1432let window: UiWindow = await driver.findWindow({actived: true}) 1433await window.close() 1434``` 1435 1436### 使用方式 1437 1438 开发者可以下载Deveco Studio创建测试工程后,在其中调用框架提供接口进行相关测试操作,测试工程创建及测试脚本执行使用指南请参见[IDE指导文档](https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ohos-openharmony-test-framework-0000001267284568)。 1439 UI测试框架使能需要执行如下命令。 1440 1441>```shell 1442> hdc shell param set persist.ace.testmode.enabled 1 1443>``` 1444### UI测试框架自构建方式 1445 1446> Ui测试框架在OpenHarmony-3.1-Release版本中未随版本编译,需手动处理,请参考[3.1-Release版本使用指导](https://gitee.com/openharmony/testfwk_arkxtest/blob/OpenHarmony-3.1-Release/README_zh.md#%E6%8E%A8%E9%80%81ui%E6%B5%8B%E8%AF%95%E6%A1%86%E6%9E%B6%E8%87%B3%E8%AE%BE%E5%A4%87)。 1447 1448开发者如需自行编译Ui测试框架代码验证子修改内容,构建命令和推送位置请参考本章节内容。 1449 1450#### 构建命令 1451 1452```shell 1453./build.sh --product-name rk3568 --build-target uitestkit 1454``` 1455#### 推送位置 1456 1457```shell 1458hdc target mount 1459hdc shell mount -o rw,remount / 1460hdc file send uitest /system/bin/uitest 1461hdc file send libuitest.z.so /system/lib/module/libuitest.z.so 1462hdc shell chmod +x /system/bin/uitest 1463``` 1464 1465### 命令行使用说明 1466 1467 开发者可以输入如下命令来实现对应功能。 1468 14691、打印使用帮助 1470 1471```shell 1472hdc shell uitest help 1473``` 1474 14752、截屏 1476 1477``` 1478hdc shell uitest screenCap 1479// 默认存储路径:/data/local/tmp,文件名:时间戳 + .png。 1480hdc shell uitest screenCap -p /data/local/tmp/1.png 1481// 指定存储路径和文件名, 只支持存放在/data/local/tmp/下。 1482``` 1483 14843、获取设备当前Ui控件树信息 1485 1486```shell 1487hdc shell uitest dumpLayout 1488// 默认存储路径:/data/local/tmp,文件名:时间戳 + .json。 1489hdc shell uitest screenCap -p /data/local/tmp/1.json 1490// 指定存储路径和文件名, 只支持存放在/data/local/tmp/下。 1491``` 1492 14934、录制Ui操作 1494 1495```shell 1496hdc shell uitest uiRecord record 1497// 将当前执行的Ui操作记录到/data/local/tmp/layout/record.csv 1498hdc shell uitest uiRecord read 1499// 将记录的Ui操作打印出来 1500``` 1501 15025、 shell命令方式注入UI模拟操作 1503> 支持操作类型:点击 双击 长按 慢滑 快滑 拖拽 输入文字 KeyEvent。 1504 1505| 配置参数名 | 配置参数含义 | 配置参数取值 | 示例 | 1506|-------------|-----------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------| 1507| click | 模拟单击操作 | point_x (必选参数,点击x坐标点)<br/> point_y (必选参数,点击y坐标点) | hdc shell uitest uiInput click point_x point_y | 1508| doubleClick | 模拟双击操作 | point_x (必选参数,双击x坐标点)<br/> point_y (必选参数,双击y坐标点) | hdc shell uitest uiInput doubleClick point_x point_y | 1509| longClick | 模拟长按操作 | point_x (必选参数,长按x坐标点)<br/> point_y (必选参数,长按y坐标点) | hdc shell uitest uiInput longClick point_x point_y | 1510| fling | 模拟快滑操作 | from_x (必选参数,滑动起点x坐标)<br/> from_y(必选参数,滑动起点y坐标)<br/> to_x(必选参数,滑动终点x坐标)<br/> to_y(必选参数,滑动终点y坐标)<br/> swipeVelocityPps_ (可选参数,滑动速度,取值范围: 200-40000, 默认值: 600, 单位: px/s)<br/> stepLength(可选参数,滑动步长,默认值:滑动距离/50, 单位: px) | hdc shell uitest uiInput fling from_x from_y to_x to_y swipeVelocityPps_ stepLength | 1511| swipe | 模拟慢滑操作 | from_x (必选参数,滑动起点x坐标)<br/> from_y(必选参数,滑动起点y坐标)<br/> to_x(必选参数,滑动终点x坐标)<br/> to_y(必选参数,滑动终点y坐标)<br/> swipeVelocityPps_ (可选参数,滑动速度,取值范围: 200-40000, 默认值: 600, 单位: px/s)) | hdc shell uitest uiInput swipe from_x from_y to_x to_y swipeVelocityPps_ | 1512| drag | 模拟拖拽操作 | from_x (必选参数,拖拽起点x坐标)<br/> from_y(必选参数,拖拽起点y坐标)<br/> to_x(必选参数,拖拽终点x坐标)<br/> to_y(必选参数,拖拽终点y坐标)<br/> swipeVelocityPps_ (可选参数,滑动速度,取值范围: 200-40000, 默认值: 600, 单位: px/s)) | hdc shell uitest uiInput drag from_x from_y to_x to_y swipeVelocityPps_ | 1513| dircFling | 模拟指定方向滑动操作 | direction (可选参数,滑动方向,可选值: [0,1,2,3], 滑动方向: [左,右,上,下],默认值: 0)<br/> swipeVelocityPps_ (可选参数,滑动速度,取值范围: 200-40000, 默认值: 600, 单位: px/s)<br/> stepLength(可选参数,滑动步长,默认值:滑动距离/50, 单位: px) | hdc shell uitest uiInput dircFling direction swipeVelocityPps_ stepLength | 1514| inputText | 模拟输入框输入文本操作 | point_x (必选参数,输入框x坐标点)<br/> point_y (必选参数,输入框y坐标点)<br/> input(输入文本) | hdc shell uitest uiInput inputText point_x point_y text | 1515| keyEvent | 模拟实体按键事件(如:键盘,电源键,返回上一级,返回桌面等),以及组合按键操作 | keyID (必选参数,实体按键对应ID)<br/> keyID2 (可选参数,实体按键对应ID) | hdc shell uitest uiInput keyEvent keyID | 1516 1517示例代码1:执行点击事件。 1518```shell 1519 hdc shell uitest uiInput click 100 100 1520``` 1521示例代码2:执行双击事件。 1522```shell 1523 hdc shell uitest uiInput doubleClick 100 100 1524``` 1525示例代码3:执行长按事件。 1526```shell 1527 hdc shell uitest uiInput longClick 100 100 1528``` 1529示例代码4:执行快滑操作。 1530```shell 1531hdc shell uitest uiInput fling 10 10 200 200 500 1532``` 1533示例代码5:执行慢滑操作。 1534```shell 1535hdc shell uitest uiInput swipe 10 10 200 200 500 1536``` 1537示例代码6:执行拖拽操作。 1538```shell 1539hdc shell uitest uiInput drag 10 10 100 100 500 1540``` 1541示例代码7:执行向左滑动操作。 1542```shell 1543hdc shell uitest uiInput dircFling 0 500 1544``` 1545示例代码8:执行向右滑动操作。 1546```shell 1547hdc shell uitest uiInput dircFling 1 600 1548``` 1549示例代码9:执行向上滑动操作。 1550```shell 1551hdc shell uitest uiInput dircFling 2 1552``` 1553示例代码10:执行向下滑动操作。 1554```shell 1555hdc shell uitest uiInput dircFling 3 1556``` 1557 1558示例代码11:执行输入框输入操作。 1559```shell 1560hdc shell uitest uiInput inputText 100 100 hello 1561``` 1562 1563示例代码12:执行返回主页操作。 1564```shell 1565hdc shell uitest uiInput keyEvent Home 1566``` 1567示例代码13:执行返回上一步操作。 1568```shell 1569hdc shell uitest uiInput keyEvent Back 1570``` 1571示例代码14:执行组合键粘贴操作。 1572```shell 1573hdc shell uitest uiInput keyEvent 2072 2038 1574``` 1575 1576### 版本信息 1577 1578| 版本号 | 功能说明 | 1579| :------ | :----------------------------------------------------------- | 1580| 3.2.2.1 | 1、增加抛滑、获取/设置屏幕方向接口<br />2、窗口处理逻辑增加不支持场景处理逻辑 | 1581| 3.2.3.0 | 1、滑动控件进行滑动查找、滑动到尾部/顶部功能优化 | 1582| 3.2.4.0 | 1、接口调用异常时会抛出错误码 | 1583| 3.2.5.0 | 1、通信机制变更 | 1584| 3.2.6.0 | 1、增加模拟鼠标操作能力接口<br />2、增加指定应用的窗口下查找目标控件接口 | 1585| 4.0.1.1 | 1、支持在daemon运行时执行uitest dumpLayout | 1586| 4.0.1.2 | 1、模拟鼠标动作、键鼠协同功能优化 | 1587| 4.0.1.3 | 1、示例代码更新<br />2、滑动控件进行滑动查找、滑动到尾部/顶部功能优化 | 1588| 4.0.1.4 | 1、可选参数传入undefined时,当作默认值处理 | 1589| 4.0.2.0 | 1、支持监听toast和dialog控件出现,使用callback的形式返回结果。 | 1590| 4.0.3.0 | 1、增加加载运行.abc文件机制。 | 1591| 4.0.4.0 | 1、支持abc_loader框架获取UI操作录制数据,屏幕数据,控件树等,并以callback的形式返回结果<br />2、修改录制数据结构 | 1592| 4.1.1.1 | 1、对接批量获取控件信息能力,缩短获取控件信息的耗时。 | 1593| 4.1.2.0 | 1、增加shell命令方式注入UI模拟操作。 | 1594| 4.1.3.0 | 1、新增命令行功能,uitest dumuLayout -a ,dump信息中包含控件的背景色、字体颜色/大小信息。 | 1595| 4.1.4.0 | 1、dump信息中增加hint与description字段。<br />2、优化多指操作。<br />3、优化查找控件的效率。<br />4、uitest uiInput执行效率提升。 | 1596| 5.0.1.0 | 1、优化swipe操作。<br />2、inputText输入中文的实现方式改为设置剪贴板数据后,长按控件点击粘贴。 | 1597