1# 自动化测试框架使用介绍 2 3## 简介 4 OpenHarmony自动化测试框架代码部件仓arkxtest,包含单元测试框架(JsUnit)、Ui测试框架(UiTest)和白盒性能测试框架(PerfTest)。 5 6 单元测试框架(JsUnit)提供单元测试用例执行能力,提供用例编写基础接口,生成对应报告,用于测试系统或应用接口。 7 8 Ui测试框架(UiTest)通过简洁易用的API提供查找和操作界面控件能力,支持用户开发基于界面操作的自动化测试脚本。 9 10 PerfTest提供基于代码段的白盒性能测试能力,支持采集指定代码段执行期间或指定场景发生时的性能数据。 11 12## 目录 13 14``` 15arkxtest 16 |-----jsunit 单元测试框架 17 |-----UiTest Ui测试框架 18 |-----perftest 白盒性能测试框架 19``` 20## 约束限制 21本模块首批接口从API version 8开始支持。后续版本的新增接口,采用上角标单独标记接口的起始版本。 22 23## 单元测试框架功能特性 24 25| No. | 特性 | 功能说明 | 26| ---- | -------- |----------------------------------------------------| 27| 1 | 基础流程 | 支持编写及异步执行基础用例。 | 28| 2 | 断言库 | 判断用例实际结果值与预期值是否相符。 | 29| 3 | Mock能力 | 支持函数级mock能力,对定义的函数进行mock后修改函数的行为,使其返回指定的值或者执行某种动作。 | 30| 4 | 数据驱动 | 提供数据驱动能力,支持复用同一个测试脚本,使用不同输入数据驱动执行。 | 31| 5 | 专项能力 | 支持测试套与用例筛选、随机执行、压力测试、超时设置、遇错即停模式,跳过,支持测试套嵌套等。 | 32 33### 使用说明 34 35#### 基础流程 36 37测试用例采用业内通用语法,describe代表一个测试套, it代表一条用例。 38 39| No. | API | 功能说明 | 40|-----| ----------------- |------------------------------------------------------------------------| 41| 1 | describe | 定义一个测试套,支持两个参数:测试套名称和测试套函数。其中测试套函数不能是异步函数。 | 42| 2 | beforeAll | 在测试套内定义一个预置条件,在所有测试用例开始前执行且仅执行一次,支持一个参数:预置动作函数。 | 43| 3 | beforeEach | 在测试套内定义一个单元预置条件,在每条测试用例开始前执行,执行次数与it定义的测试用例数一致,支持一个参数:预置动作函数。 | 44| 4 | afterEach | 在测试套内定义一个单元清理条件,在每条测试用例结束后执行,执行次数与it定义的测试用例数一致,支持一个参数:清理动作函数。 | 45| 5 | afterAll | 在测试套内定义一个清理条件,在所有测试用例结束后执行且仅执行一次,支持一个参数:清理动作函数。 | 46| 6 | beforeItSpecified | @since1.0.15在测试套内定义一个单元预置条件,仅在指定测试用例开始前执行,支持两个参数:单个用例名称或用例名称数组、预置动作函数。 | 47| 7 | afterItSpecified | @since1.0.15在测试套内定义一个单元清理条件,仅在指定测试用例结束后执行,支持两个参数:单个用例名称或用例名称数组、清理动作函数。 | 48| 8 | it | 定义一条测试用例,支持三个参数:用例名称,过滤参数和用例函数。 | 49| 9 | expect | 支持bool类型判断等多种断言方法。 | 50| 10 | xdescribe | @since1.0.17定义一个跳过的测试套,支持两个参数:测试套名称和测试套函数。 | 51| 11 | xit | @since1.0.17定义一条跳过的测试用例,支持三个参数:用例名称,过滤参数和用例函数。 | 52 53 54beforeItSpecified, afterItSpecified 示例代码: 55 56```javascript 57import { describe, it, expect, beforeItSpecified, afterItSpecified } from '@ohos/hypium'; 58export default function beforeItSpecifiedTest() { 59 describe('beforeItSpecifiedTest', () => { 60 beforeItSpecified(['String_assertContain_success'], () => { 61 const num:number = 1; 62 expect(num).assertEqual(1); 63 }) 64 afterItSpecified(['String_assertContain_success'], async (done: Function) => { 65 const str:string = 'abc'; 66 setTimeout(()=>{ 67 try { 68 expect(str).assertContain('b'); 69 } catch (error) { 70 console.error(`error message ${JSON.stringify(error)}`); 71 } 72 done(); 73 }, 1000) 74 }) 75 it('String_assertContain_success', 0, () => { 76 let a: string = 'abc'; 77 let b: string = 'b'; 78 expect(a).assertContain(b); 79 expect(a).assertEqual(a); 80 }) 81 }) 82} 83``` 84 85#### 断言库 86 87##### 断言功能列表 88 89 90| No. | API | 功能说明 | 91|:----| :------------------|-------------------------------------------------------------| 92| 1 | assertClose | 检验actualvalue和expectvalue(0)的接近程度是否是expectValue(1)。 | 93| 2 | assertContain | 检验actualvalue中是否包含expectvalue。 | 94| 3 | assertEqual | 检验actualvalue是否等于expectvalue[0]。 | 95| 4 | assertFail | 抛出一个错误。 | 96| 5 | assertFalse | 检验actualvalue是否是false。 | 97| 6 | assertTrue | 检验actualvalue是否是true。 | 98| 7 | assertInstanceOf | 检验actualvalue是否是expectvalue类型,支持基础类型。 | 99| 8 | assertLarger | 检验actualvalue是否大于expectvalue。 | 100| 9 | assertLargerOrEqual | 检验actualvalue是否大于等于expectvalue。 | 101| 10 | assertLess | 检验actualvalue是否小于expectvalue。 | 102| 11 | assertLessOrEqual | 检验actualvalue是否小于等于expectvalue。 | 103| 12 | assertNull | 检验actualvalue是否是null。 | 104| 13 | assertThrowError | 检验actualvalue抛出Error内容是否是expectValue。 | 105| 14 | assertUndefined | 检验actualvalue是否是undefined。 | 106| 15 | assertNaN | @since1.0.4 检验actualvalue是否是一个NaN。 | 107| 16 | assertNegUnlimited | @since1.0.4 检验actualvalue是否等于Number.NEGATIVE_INFINITY。 | 108| 17 | assertPosUnlimited | @since1.0.4 检验actualvalue是否等于Number.POSITIVE_INFINITY。 | 109| 18 | assertDeepEquals | @since1.0.4 检验actualvalue和expectvalue是否完全相等。 | 110| 19 | assertPromiseIsPending | @since1.0.4 判断promise是否处于Pending状态。 | 111| 20 | assertPromiseIsRejected | @since1.0.4 判断promise是否处于Rejected状态。 | 112| 21 | assertPromiseIsRejectedWith | @since1.0.4 判断promise是否处于Rejected状态,并且比较执行的结果值。 | 113| 22 | assertPromiseIsRejectedWithError | @since1.0.4 判断promise是否处于Rejected状态并有异常,同时比较异常的类型和message值。 | 114| 23 | assertPromiseIsResolved | @since1.0.4 判断promise是否处于Resolved状态。 | 115| 24 | assertPromiseIsResolvedWith | @since1.0.4 判断promise是否处于Resolved状态,并且比较执行的结果值。 | 116| 25 | not | @since1.0.4 断言取反,支持上面所有的断言功能。 | 117| 26 | message | @since1.0.17自定义断言异常信息。 | 118 119expect断言示例代码: 120 121```javascript 122import { describe, it, expect } from '@ohos/hypium'; 123 124export default function expectTest() { 125 describe('expectTest', () => { 126 it('assertCloseTest', 0, () => { 127 let a: number = 100; 128 let b: number = 0.1; 129 expect(a).assertClose(99, b); 130 }) 131 it('assertContain_1', 0, () => { 132 let a = "abc"; 133 expect(a).assertContain('b'); 134 }) 135 it('assertContain_2', 0, () => { 136 let a = [1, 2, 3]; 137 expect(a).assertContain(1); 138 }) 139 it('assertEqualTest', 0, () => { 140 expect(3).assertEqual(3); 141 }) 142 it('assertFailTest', 0, () => { 143 expect().assertFail() // 用例失败; 144 }) 145 it('assertFalseTest', 0, () => { 146 expect(false).assertFalse(); 147 }) 148 it('assertTrueTest', 0, () => { 149 expect(true).assertTrue(); 150 }) 151 it('assertInstanceOfTest', 0, () => { 152 let a: string = 'strTest'; 153 expect(a).assertInstanceOf('String'); 154 }) 155 it('assertLargerTest', 0, () => { 156 expect(3).assertLarger(2); 157 }) 158 it('assertLessTest', 0, () => { 159 expect(2).assertLess(3); 160 }) 161 it('assertNullTest', 0, () => { 162 expect(null).assertNull(); 163 }) 164 it('assertThrowErrorTest', 0, () => { 165 expect(() => { 166 throw new Error('test') 167 }).assertThrowError('test'); 168 }) 169 it('assertUndefinedTest', 0, () => { 170 expect(undefined).assertUndefined(); 171 }) 172 it('assertLargerOrEqualTest', 0, () => { 173 expect(3).assertLargerOrEqual(3); 174 }) 175 it('assertLessOrEqualTest', 0, () => { 176 expect(3).assertLessOrEqual(3); 177 }) 178 it('assertNaNTest', 0, () => { 179 expect(Number.NaN).assertNaN(); // true 180 }) 181 it('assertNegUnlimitedTest', 0, () => { 182 expect(Number.NEGATIVE_INFINITY).assertNegUnlimited(); // true 183 }) 184 it('assertPosUnlimitedTest', 0, () => { 185 expect(Number.POSITIVE_INFINITY).assertPosUnlimited(); // true 186 }) 187 it('deepEquals_null_true', 0, () => { 188 // Defines a variety of assertion methods, which are used to declare expected boolean conditions. 189 expect(null).assertDeepEquals(null); 190 }) 191 it('deepEquals_array_not_have_true', 0, () => { 192 // Defines a variety of assertion methods, which are used to declare expected boolean conditions. 193 const a: Array<number> = []; 194 const b: Array<number> = []; 195 expect(a).assertDeepEquals(b); 196 }) 197 it('deepEquals_map_equal_length_success', 0, () => { 198 // Defines a variety of assertion methods, which are used to declare expected boolean conditions. 199 const a: Map<number, number> = new Map(); 200 const b: Map<number, number> = new Map(); 201 a.set(1, 100); 202 a.set(2, 200); 203 b.set(1, 100); 204 b.set(2, 200); 205 expect(a).assertDeepEquals(b); 206 }) 207 it("deepEquals_obj_success_1", 0, () => { 208 const a: SampleTest = {x: 1}; 209 const b: SampleTest = {x: 1}; 210 expect(a).assertDeepEquals(b); 211 }) 212 it("deepEquals_regExp_success_0", 0, () => { 213 const a: RegExp = new RegExp("/test/"); 214 const b: RegExp = new RegExp("/test/"); 215 expect(a).assertDeepEquals(b); 216 }) 217 it('assertPromiseIsPendingTest', 0, async () => { 218 let p: Promise<void> = new Promise<void>(() => {}); 219 await expect(p).assertPromiseIsPending(); 220 }) 221 it('assertPromiseIsRejectedTest', 0, async () => { 222 let info: PromiseInfo = {res: "no"}; 223 let p: Promise<PromiseInfo> = Promise.reject(info); 224 await expect(p).assertPromiseIsRejected(); 225 }) 226 it('assertPromiseIsRejectedWithTest', 0, async () => { 227 let info: PromiseInfo = {res: "reject value"}; 228 let p: Promise<PromiseInfo> = Promise.reject(info); 229 await expect(p).assertPromiseIsRejectedWith(info); 230 }) 231 it('assertPromiseIsRejectedWithErrorTest', 0, async () => { 232 let p1: Promise<TypeError> = Promise.reject(new TypeError('number')); 233 await expect(p1).assertPromiseIsRejectedWithError(TypeError); 234 }) 235 it('assertPromiseIsResolvedTest', 0, async () => { 236 let info: PromiseInfo = {res: "result value"}; 237 let p: Promise<PromiseInfo> = Promise.resolve(info); 238 await expect(p).assertPromiseIsResolved(); 239 }) 240 it('assertPromiseIsResolvedWithTest', 0, async () => { 241 let info: PromiseInfo = {res: "result value"}; 242 let p: Promise<PromiseInfo> = Promise.resolve(info); 243 await expect(p).assertPromiseIsResolvedWith(info); 244 }) 245 it("test_message", 0, () => { 246 expect(1).message('1 is not equal 2!').assertEqual(2); // fail 247 }) 248 }) 249} 250 251interface SampleTest { 252 x: number; 253} 254 255interface PromiseInfo { 256 res: string; 257} 258``` 259 260##### 自定义断言@since1.0.18 261 262示例代码: 263 264```javascript 265import { describe, Assert, beforeAll, expect, Hypium, it } from '@ohos/hypium'; 266 267// custom.ets 268interface customAssert extends Assert { 269 // 自定义断言声明 270 myAssertEqual(expectValue: boolean): void; 271} 272 273//自定义断言实现 274let myAssertEqual = (actualValue: boolean, expectValue: boolean) => { 275 interface R { 276 pass: boolean, 277 message: string 278} 279 280let result: R = { 281 pass: true, 282 message: 'just is a msg' 283} 284 285let compare = () => { 286 if (expectValue === actualValue) { 287 result.pass = true; 288 result.message = ''; 289 } else { 290 result.pass = false; 291 result.message = 'expectValue !== actualValue!'; 292 } 293 return result; 294} 295result = compare(); 296return result; 297} 298 299export default function customAssertTest() { 300 describe('customAssertTest', () => { 301 beforeAll(() => { 302 //注册自定义断言,只有先注册才可以使用 303 Hypium.registerAssert(myAssertEqual); 304 }) 305 it('assertContain1', 0, () => { 306 let a = true; 307 let b = true; 308 (expect(a) as customAssert).myAssertEqual(b); 309 Hypium.unregisterAssert(myAssertEqual); 310 }) 311 it('assertContain2', 0, () => { 312 Hypium.registerAssert(myAssertEqual); 313 let a = true; 314 let b = true; 315 (expect(a) as customAssert).myAssertEqual(b); 316 // 注销自定义断言,注销以后就无法使用 317 Hypium.unregisterAssert(myAssertEqual); 318 try { 319 (expect(a) as customAssert).myAssertEqual(b); 320 }catch(e) { 321 expect(e.message).assertEqual("myAssertEqual is unregistered"); 322 } 323 }) 324 }) 325} 326``` 327 328#### 异步代码测试 329 330 **异步测试错误示例代码:** 331```javascript 332import { describe, it, expect } from '@ohos/hypium'; 333 334async function callBack(fn: Function) { 335 setTimeout(fn, 5000, 'done') 336} 337 338export default function callBackErrorTest() { 339 describe('callBackErrorTest', () => { 340 it('callBackErrorTest', 0, () => { 341 callBack((result: string) => { 342 try { 343 // 用例失败 344 expect().assertFail(); 345 } catch (e) { 346 } finally { 347 } 348 }) 349 }) 350 }) 351} 352``` 353> - 上述测试用例中,测试函数结束后, 回调函数才执行, 导致用例结果错误。 354> - 当使用框架测试异步代码时,框架需要知道它测试的代码何时完成。以确保测试用例结果正常统计,测试框架用以下两种方式来处理这个问题。 355 356##### Async/Await 357> 使用 Async关键字定义一个异步测试函数,在测试函数中使用await等待测试函数完成。 358 359**Promise示例代码:** 360 361```javascript 362import { describe, it, expect } from '@ohos/hypium'; 363 364async function method_1() { 365 return new Promise<string>((res: Function, rej: Function) => { 366 //做一些异步操作 367 setTimeout(() => { 368 console.log('执行'); 369 res('method_1_call'); 370 }, 5000); 371 }); 372} 373 374export default function abilityTest() { 375 describe('ActsAbilityTest', () => { 376 it('assertContain', 0, async () => { 377 let result = await method_1(); 378 expect(result).assertEqual('method_1_call'); 379 }) 380 }) 381} 382``` 383##### done 函数 384> - done函数是测试函数的一个可选回调参数,在测试用例中手动调用,测试框架将等待done回调被调用,然后才完成测试。 385> - 当测试函数中定义done函数参数时,测试用例中必须手动调用done函数,否则用例失败会出现超时错误。 386 387 388**Promise回调示例代码:** 389```javascript 390import { describe, it, expect } from '@ohos/hypium'; 391async function method_1() { 392 return new Promise<string>((res: Function, rej: Function) => { 393 //做一些异步操作 394 setTimeout(() => { 395 console.log('执行'); 396 res('method_1_call'); 397 }, 5000); 398 }); 399} 400 401export default function abilityTest() { 402 describe('Promise_Done', () => { 403 it('Promise_Done', 0,(done: Function) => { 404 method_1().then((result: string) => { 405 try { 406 expect(result).assertEqual('method_1_call'); 407 } catch (e) { 408 } finally { 409 // 调用done函数,用例执行完成,必须手动调用done函数,否则出现超时错误。 410 done() 411 } 412 }) 413 414 }) 415 }) 416} 417``` 418 419**回调函数示例代码:** 420```javascript 421import { describe, it, expect } from '@ohos/hypium'; 422 423async function callBack(fn: Function) { 424 setTimeout(fn, 5000, 'done') 425} 426 427export default function callBackTestTest() { 428 describe('CallBackTest', () => { 429 it('CallBackTest_001', 0, (done: Function) => { 430 callBack( (result: string) => { 431 try { 432 expect(result).assertEqual('done'); 433 } catch (e) { 434 } finally { 435 // 调用done函数,用例执行完成,必须手动调用done函数,否则出现超时错误。 436 done() 437 } 438 }) 439 }) 440 }) 441} 442``` 443 444#### SysTestKit的公共能力 445 446| No. | API | 功能说明 | 447|:----| :------------------|-------------------| 448| 1 | getDescribeName | 获取当前测试用例所属的测试套名称。 | 449| 2 | getItName | 获取当初测试用例名称。 | 450| 3 | getItAttribute | 获取当初测试用例等级。 | 451| 4 | actionStart | 添加用例执行过程打印自定义日志。 | 452| 5 | actionEnd | 添加用例执行过程打印自定义日志。 | 453| 6 | existKeyword | 检测hilog日志中是否打印。 | 454 455##### 获取当前测试用例所属的测试套名称 456 457示例代码: 458```javascript 459import { describe, it, expect, SysTestKit } from '@ohos/hypium'; 460import hilog from '@ohos.hilog'; 461const domain = 0; 462const tag = 'SysTestKitTest' 463export default function abilityTest() { 464 describe('SysTestKitTest', () => { 465 466 it("testGetDescribeName", 0, () => { 467 hilog.debug(domain, tag, `testGetDescribeName start`); 468 const describeName = SysTestKit.getDescribeName(); 469 hilog.debug(domain, tag, `testGetDescribeName describeName, ${describeName}`); 470 expect(describeName).assertEqual('SysTestKitTest'); 471 hilog.debug(domain, tag, `testGetDescribeName end`); 472 }) 473 }) 474} 475``` 476 477##### 获取当初测试用例名称 478 479示例代码: 480```javascript 481import { describe, it, expect, SysTestKit } from '@ohos/hypium'; 482import hilog from '@ohos.hilog'; 483const domain = 0; 484const tag = 'SysTestKitTest' 485export default function abilityTest() { 486 describe('SysTestKitTest', () => { 487 488 it("testGetItName", 0, () => { 489 hilog.debug(domain, tag, `testGetDescribeName start`); 490 const itName = SysTestKit.getItName(); 491 hilog.debug(domain, tag, `testGetDescribeName itName, ${itName}`); 492 expect(itName).assertEqual('testGetItName'); 493 hilog.debug(domain, tag, `testGetDescribeName end`); 494 }) 495 }) 496} 497``` 498 499##### 获取当初测试用例级别 500 501示例代码: 502```javascript 503import { describe, it, expect, SysTestKit, TestType, Level, Size } from '@ohos/hypium'; 504import hilog from '@ohos.hilog'; 505const domain = 0; 506const tag = 'SysTestKitTest' 507export default function abilityTest() { 508 describe('SysTestKitTest', () => { 509 510 it("testGetItAttribute", TestType.FUNCTION | Size.SMALLTEST | Level.LEVEL0, () => { 511 hilog.debug(domain, tag, `testGetItAttribute start`); 512 const testType: TestType | Size | Level = SysTestKit.getItAttribute(); 513 hilog.debug(domain, tag, `testGetDescribeName testType, ${ testType }`); 514 expect(testType).assertEqual(TestType.FUNCTION | Size.SMALLTEST | Level.LEVEL0); 515 hilog.debug(domain, tag, `testGetItAttribute end`); 516 }) 517 }) 518} 519``` 520 521##### 添加自定义打印日志。 522 523示例代码: 524```javascript 525import { describe, it, expect, SysTestKit, TestType, Level, Size } from '@ohos/hypium'; 526import hilog from '@ohos.hilog'; 527const domain = 0; 528const tag = 'SysTestKitTest' 529export default function abilityTest() { 530 describe('SysTestKitTest', () => { 531 532 it("testActionStart", TestType.FUNCTION | Size.SMALLTEST | Level.LEVEL0, () => { 533 hilog.debug(domain, tag, `testActionStart start `); 534 SysTestKit.actionStart('testActionStart 自定义日志 '); 535 SysTestKit.actionEnd('testActionStart end 自定义日志 '); 536 hilog.debug(domain, tag, `testActionStart end`); 537 }) 538 }) 539} 540``` 541 542##### 检测hilog日志中是否打印。 543 544示例代码: 545```javascript 546import { describe, it, expect, SysTestKit, TestType, Level, Size } from '@ohos/hypium'; 547import hilog from '@ohos.hilog'; 548const domain = 0; 549const tag = 'SysTestKitTest' 550 551function logTest() { 552 hilog.debug(domain, 'test', `logTest called selfTest`); 553} 554 555export default function abilityTest() { 556 describe('SysTestKitTest', () => { 557 558 it("testExistKeyword", TestType.FUNCTION | Size.SMALLTEST | Level.LEVEL0, async () => { 559 hilog.debug(domain, tag, `testExistKeyword start `); 560 logTest(); 561 const isCalled = await SysTestKit.existKeyword('logTest'); 562 hilog.debug(domain, tag, `testExistKeyword isCalled, ${isCalled} `); 563 expect(isCalled).assertTrue(); 564 hilog.debug(domain, tag, `testExistKeyword end`); 565 }) 566 }) 567} 568``` 569 570#### Mock能力 571 572##### 约束限制 573 574单元测试框架Mock能力从npm包[1.0.1版本](https://ohpm.openharmony.cn/#/cn/detail/@ohos%2Fhypium)开始支持,需修改源码工程中package.info中配置依赖npm包版本号后使用。 575> - 仅支持mock自定义对象,不支持mock系统API对象。 576> - 不支持mock对象的私有函数。 577- **接口列表:** 578 579| No. | API | 功能说明 | 580| --- | --- |-------------------------------------------------------------------------------------------------------------------------------------------------| 581| 1 | mockFunc(obj: object, f:function) | mock某个类的对象obj的函数f,那么需要传两个参数:obj和f,支持使用异步函数(说明:对mock而言原函数实现是同步或异步没太多区别,因为mock并不关注原函数的实现)。 | 582| 2 | when(mockedfunc:function) | 对传入后方法做检查,检查是否被mock并标记过,返回的是一个方法声明。 | 583| 3 | afterReturn(x:value) | 设定预期返回一个自定义的值value,比如某个字符串或者一个promise。 | 584| 4 | afterReturnNothing() | 设定预期没有返回值,即 undefined。 | 585| 5 | afterAction(x:action) | 设定预期返回一个函数执行的操作。 | 586| 6 | afterThrow(x:msg) | 设定预期抛出异常,并指定异常msg。 | 587| 7 | clear(obj: object) | 用例执行完毕后,进行数据mocker对象的还原处理(还原之后对象恢复被mock之前的功能)。 | 588| 8 | any | 设定用户传任何类型参数(undefined和null除外),执行的结果都是预期的值,使用ArgumentMatchers.any方式调用。 | 589| 9 | anyString | 设定用户传任何字符串参数,执行的结果都是预期的值,使用ArgumentMatchers.anyString方式调用。 | 590| 10 | anyBoolean | 设定用户传任何boolean类型参数,执行的结果都是预期的值,使用ArgumentMatchers.anyBoolean方式调用。 | 591| 11 | anyFunction | 设定用户传任何function类型参数,执行的结果都是预期的值,使用ArgumentMatchers.anyFunction方式调用。 | 592| 12 | anyNumber | 设定用户传任何数字类型参数,执行的结果都是预期的值,使用ArgumentMatchers.anyNumber方式调用。 | 593| 13 | anyObj | 设定用户传任何对象类型参数,执行的结果都是预期的值,使用ArgumentMatchers.anyObj方式调用。 | 594| 14 | matchRegexs(Regex) | 设定用户传任何正则表达式类型参数Regex,执行的结果都是预期的值,使用ArgumentMatchers.matchRegexs(Regex)方式调用。 | 595| 15 | verify(methodName, argsArray) | 验证methodName(函数名字符串)所对应的函数和其参数列表argsArray的执行行为是否符合预期,返回一个VerificationMode:一个提供验证模式的类,它有times(count)、once()、atLeast(x)、atMost(x)、never()等函数可供选择。 | 596| 16 | times(count) | 验证行为调用过count次。 | 597| 17 | once() | 验证行为调用过一次。 | 598| 18 | atLeast(count) | 验证行为至少调用过count次。 | 599| 19 | atMost(count) | 验证行为至多调用过count次。 | 600| 20 | never | 验证行为从未发生过。 | 601| 21 | ignoreMock(obj, method) | 使用ignoreMock可以还原obj对象中被mock后的函数,对被mock后的函数有效。 | 602| 22 | clearAll() | 用例执行完毕后,进行数据和内存清理,不会还原obj对象中被mock后的函数。 | | 603 604- **使用示例:** 605 606用户可以通过以下方式引入mock模块进行测试用例编写: 607 608- **须知:** 609使用时候必须引入的mock能力模块: MockKit,when,根据自己用例需要引入断言能力api。 610例如:`import { describe, expect, it, MockKit, when} from '@ohos/hypium'` 611 612**示例1: afterReturn 的使用** 613 614```javascript 615import { describe, expect, it, MockKit, when } from '@ohos/hypium'; 616 617class ClassName { 618 constructor() { 619 } 620 621 method_1(arg: string) { 622 return '888888'; 623 } 624 625 method_2(arg: string) { 626 return '999999'; 627 } 628} 629export default function afterReturnTest() { 630 describe('afterReturnTest', () => { 631 it('afterReturnTest', 0, () => { 632 console.info("it1 begin"); 633 // 1.创建一个mock能力的对象MockKit 634 let mocker: MockKit = new MockKit(); 635 // 2.定类ClassName,里面两个函数,然后创建一个对象claser 636 let claser: ClassName = new ClassName(); 637 // 3.进行mock操作,比如需要对ClassName类的method_1函数进行mock 638 let mockfunc: Function = mocker.mockFunc(claser, claser.method_1); 639 // 4.期望claser.method_1函数被mock后, 以'test'为入参时调用函数返回结果'1' 640 when(mockfunc)('test').afterReturn('1'); 641 // 5.对mock后的函数进行断言,看是否符合预期 642 // 执行成功案例,参数为'test' 643 expect(claser.method_1('test')).assertEqual('1'); // 执行通过 644 }) 645 }) 646} 647``` 648- **须知:** 649`when(mockfunc)('test').afterReturn('1');` 650这句代码中的`('test')`是mock后的函数需要传递的匹配参数,目前支持传递多个参数。 651`afterReturn('1')`是用户需要预期返回的结果。 652有且只有在参数是`('test')`的时候,执行的结果才是用户自定义的预期结果。 653 654**示例2: afterReturnNothing 的使用** 655 656```javascript 657import { describe, expect, it, MockKit, when } from '@ohos/hypium'; 658 659class ClassName { 660 constructor() { 661 } 662 663 method_1(arg: string) { 664 return '888888'; 665 } 666 667 method_2(arg: string) { 668 return '999999'; 669 } 670} 671export default function afterReturnNothingTest() { 672 describe('afterReturnNothingTest', () => { 673 it('testMockfunc', 0, () => { 674 console.info("it1 begin"); 675 // 1.创建一个mock能力的对象MockKit 676 let mocker: MockKit = new MockKit(); 677 // 2.定类ClassName,里面两个函数,然后创建一个对象claser 678 let claser: ClassName = new ClassName(); 679 // 3.进行mock操作,比如需要对ClassName类的method_1函数进行mock 680 let mockfunc: Function = mocker.mockFunc(claser, claser.method_1); 681 // 4.期望claser.method_1函数被mock后, 以'test'为入参时调用函数返回结果undefined 682 when(mockfunc)('test').afterReturnNothing(); 683 // 5.对mock后的函数进行断言,看是否符合预期,注意选择跟第4步中对应的断言方法 684 // 执行成功案例,参数为'test',这时候执行原对象claser.method_1的方法,会发生变化 685 // 这时候执行的claser.method_1不会再返回'888888',而是设定的afterReturnNothing()生效// 不返回任何值; 686 expect(claser.method_1('test')).assertUndefined(); // 执行通过 687 }) 688 }) 689} 690``` 691 692**示例3: 设定参数类型为any ,即接受任何参数(undefined和null除外)的使用** 693 694 695- **须知:** 696需要引入ArgumentMatchers类,即参数匹配器,例如:ArgumentMatchers.any 697 698```javascript 699import { describe, expect, it, MockKit, when, ArgumentMatchers } from '@ohos/hypium'; 700 701class ClassName { 702 constructor() { 703 } 704 705 method_1(arg: string) { 706 return '888888'; 707 } 708 709 method_2(arg: string) { 710 return '999999'; 711 } 712} 713export default function argumentMatchersAnyTest() { 714 describe('argumentMatchersAnyTest', () => { 715 it('testMockfunc', 0, () => { 716 console.info("it1 begin"); 717 // 1.创建一个mock能力的对象MockKit 718 let mocker: MockKit = new MockKit(); 719 // 2.定类ClassName,里面两个函数,然后创建一个对象claser 720 let claser: ClassName = new ClassName(); 721 // 3.进行mock操作,比如需要对ClassName类的method_1函数进行mock 722 let mockfunc: Function = mocker.mockFunc(claser, claser.method_1); 723 // 4.期望claser.method_1函数被mock后, 以任何参数调用函数时返回结果'1' 724 when(mockfunc)(ArgumentMatchers.any).afterReturn('1'); 725 // 5.对mock后的函数进行断言,看是否符合预期,注意选择跟第4步中对应的断言方法 726 // 执行成功的案例1,传参为字符串类型 727 expect(claser.method_1('test')).assertEqual('1'); // 用例执行通过。 728 // 执行成功的案例2,传参为数字类型123 729 expect(claser.method_1("123")).assertEqual('1');// 用例执行通过。 730 // 执行成功的案例3,传参为boolean类型true 731 expect(claser.method_1("true")).assertEqual('1');// 用例执行通过。 732 }) 733 }) 734} 735``` 736 737**示例4: 设定参数类型ArgumentMatchers的使用** 738 739```javascript 740import { describe, expect, it, MockKit, when, ArgumentMatchers } from '@ohos/hypium'; 741 742class ClassName { 743 constructor() { 744 } 745 746 method_1(arg: string) { 747 return '888888'; 748 } 749 750 method_2(arg: string) { 751 return '999999'; 752 } 753} 754export default function argumentMatchersTest() { 755 describe('argumentMatchersTest', () => { 756 it('testMockfunc', 0, () => { 757 console.info("it1 begin"); 758 // 1.创建一个mock能力的对象MockKit 759 let mocker: MockKit = new MockKit(); 760 // 2.定类ClassName,里面两个函数,然后创建一个对象claser 761 let claser: ClassName = new ClassName(); 762 // 3.进行mock操作,比如需要对ClassName类的method_1函数进行mock 763 let mockfunc: Function = mocker.mockFunc(claser, claser.method_1); 764 // 4.期望claser.method_1函数被mock后, 以任何string类型为参数调用函数时返回结果'1' 765 when(mockfunc)(ArgumentMatchers.anyString).afterReturn('1'); 766 // 4.对mock后的函数进行断言,看是否符合预期,注意选择跟第4步中对应的断言方法 767 // 执行成功的案例,传参为字符串类型 768 expect(claser.method_1('test')).assertEqual('1'); // 用例执行通过。 769 expect(claser.method_1('abc')).assertEqual('1'); // 用例执行通过。 770 }) 771 }) 772} 773``` 774 775**示例5: 设定参数类型为matchRegexs(Regex)等 的使用** 776```javascript 777import { describe, expect, it, MockKit, when, ArgumentMatchers } from '@ohos/hypium'; 778 779class ClassName { 780 constructor() { 781 } 782 783 method_1(arg: string) { 784 return '888888'; 785 } 786 787 method_2(arg: string) { 788 return '999999'; 789 } 790} 791export default function matchRegexsTest() { 792 describe('matchRegexsTest', () => { 793 it('testMockfunc', 0, () => { 794 console.info("it1 begin"); 795 // 1.创建一个mock能力的对象MockKit 796 let mocker: MockKit = new MockKit(); 797 let claser: ClassName = new ClassName(); 798 // 2.进行mock操作,比如需要对ClassName类的method_1函数进行mock 799 let mockfunc: Function = mocker.mockFunc(claser, claser.method_1); 800 // 3.期望claser.method_1函数被mock后, 以"test"为入参调用函数时返回结果'1' 801 when(mockfunc)(ArgumentMatchers.matchRegexs(new RegExp("test"))).afterReturn('1'); 802 // 4.对mock后的函数进行断言,看是否符合预期,注意选择跟第4步中对应的断言方法 803 // 执行成功的案例,传参为字符串类型 804 expect(claser.method_1('test')).assertEqual('1'); // 用例执行通过。 805 }) 806 }) 807} 808``` 809 810**示例6: 验证功能 Verify函数的使用** 811```javascript 812import { describe, it, MockKit } from '@ohos/hypium'; 813 814class ClassName { 815 constructor() { 816 } 817 818 method_1(...arg: string[]) { 819 return '888888'; 820 } 821 822 method_2(...arg: string[]) { 823 return '999999'; 824 } 825} 826export default function verifyTest() { 827 describe('verifyTest', () => { 828 it('testMockfunc', 0, () => { 829 console.info("it1 begin"); 830 // 1.创建一个mock能力的对象MockKit 831 let mocker: MockKit = new MockKit(); 832 // 2.然后创建一个对象claser 833 let claser: ClassName = new ClassName(); 834 // 3.进行mock操作,比如需要对ClassName类的method_1和method_2两个函数进行mock 835 mocker.mockFunc(claser, claser.method_1); 836 mocker.mockFunc(claser, claser.method_2); 837 // 4.方法调用如下 838 claser.method_1('abc', 'ppp'); 839 claser.method_1('abc'); 840 claser.method_1('xyz'); 841 claser.method_1(); 842 claser.method_1('abc', 'xxx', 'yyy'); 843 claser.method_1(); 844 claser.method_2('111'); 845 claser.method_2('111', '222'); 846 // 5.现在对mock后的两个函数进行验证,验证method_2,参数为'111'执行过一次 847 mocker.verify('method_2',['111']).once(); // 执行success 848 }) 849 }) 850} 851``` 852 853**示例7: ignoreMock(obj, method) 忽略函数的使用** 854```javascript 855import { describe, expect, it, MockKit, when, ArgumentMatchers } from '@ohos/hypium'; 856 857class ClassName { 858 constructor() { 859 } 860 861 method_1(...arg: number[]) { 862 return '888888'; 863 } 864 865 method_2(...arg: number[]) { 866 return '999999'; 867 } 868} 869export default function ignoreMockTest() { 870 describe('ignoreMockTest', () => { 871 it('testMockfunc', 0, () => { 872 console.info("it1 begin"); 873 // 1.创建一个mock能力的对象MockKit 874 let mocker: MockKit = new MockKit(); 875 // 2.创建一个对象claser 876 let claser: ClassName = new ClassName(); 877 // 3.进行mock操作,比如需要对ClassName类的method_1和method_2两个函数进行mock 878 let func_1: Function = mocker.mockFunc(claser, claser.method_1); 879 let func_2: Function = mocker.mockFunc(claser, claser.method_2); 880 // 4.期望claser.method_1函数被mock后, 以number类型为入参时调用函数返回结果'4' 881 when(func_1)(ArgumentMatchers.anyNumber).afterReturn('4'); 882 // 4.期望claser.method_2函数被mock后, 以number类型为入参时调用函数返回结果'5' 883 when(func_2)(ArgumentMatchers.anyNumber).afterReturn('5'); 884 // 5.方法调用如下 885 expect(claser.method_1(123)).assertEqual("4"); 886 expect(claser.method_2(456)).assertEqual("5"); 887 // 6.现在对mock后的两个函数的其中一个函数method_1进行忽略处理(原理是就是还原) 888 mocker.ignoreMock(claser, claser.method_1); 889 // 7.然后再去调用 claser.method_1函数,用断言测试結果 890 expect(claser.method_1(123)).assertEqual('888888'); 891 }) 892 }) 893} 894``` 895 896**示例8: clear(obj)函数的使用** 897 898```javascript 899import { describe, expect, it, MockKit, when, ArgumentMatchers } from '@ohos/hypium'; 900 901class ClassName { 902 constructor() { 903 } 904 905 method_1(...arg: number[]) { 906 return '888888'; 907 } 908 909 method_2(...arg: number[]) { 910 return '999999'; 911 } 912} 913export default function clearTest() { 914 describe('clearTest', () => { 915 it('testMockfunc', 0, () => { 916 console.info("it1 begin"); 917 // 1.创建一个mock能力的对象MockKit 918 let mocker: MockKit = new MockKit(); 919 // 2.创建一个对象claser 920 let claser: ClassName = new ClassName(); 921 // 3.进行mock操作,比如需要对ClassName类的method_1和method_2两个函数进行mock 922 let func_1: Function = mocker.mockFunc(claser, claser.method_1); 923 let func_2: Function = mocker.mockFunc(claser, claser.method_2); 924 // 4.期望claser.method_1函数被mock后, 以任何number类型为参数调用函数时返回结果'4' 925 when(func_1)(ArgumentMatchers.anyNumber).afterReturn('4'); 926 // 4.期望claser.method_2函数被mock后, 以任何number类型为参数调用函数时返回结果'5' 927 when(func_2)(ArgumentMatchers.anyNumber).afterReturn('5'); 928 // 5.方法调用如下 929 expect(claser.method_1(123)).assertEqual('4'); 930 expect(claser.method_2(123)).assertEqual('5'); 931 // 6.清除obj上所有的mock能力(原理是就是还原) 932 mocker.clear(claser); 933 // 7.然后再去调用 claser.method_1,claser.method_2 函数,测试结果 934 expect(claser.method_1(123)).assertEqual('888888'); 935 expect(claser.method_2(123)).assertEqual('999999'); 936 }) 937 }) 938} 939``` 940 941**示例9: afterThrow(msg)函数的使用** 942 943```javascript 944import { describe, expect, it, MockKit, when } from '@ohos/hypium'; 945 946class ClassName { 947 constructor() { 948 } 949 950 method_1(arg: string) { 951 return '888888'; 952 } 953} 954export default function afterThrowTest() { 955 describe('afterThrowTest', () => { 956 it('testMockfunc', 0, () => { 957 console.info("it1 begin"); 958 // 1.创建一个mock能力的对象MockKit 959 let mocker: MockKit = new MockKit(); 960 // 2.创建一个对象claser 961 let claser: ClassName = new ClassName(); 962 // 3.进行mock操作,比如需要对ClassName类的method_1函数进行mock 963 let mockfunc: Function = mocker.mockFunc(claser, claser.method_1); 964 // 4.期望claser.method_1函数被mock后, 以'test'为参数调用函数时抛出error xxx异常 965 when(mockfunc)('test').afterThrow('error xxx'); 966 // 5.执行mock后的函数,捕捉异常并使用assertEqual对比msg否符合预期 967 try { 968 claser.method_1('test'); 969 } catch (e) { 970 expect(e).assertEqual('error xxx'); // 执行通过 971 } 972 }) 973 }) 974} 975``` 976 977**示例10: mock异步返回promise对象的使用** 978 979```javascript 980import { describe, expect, it, MockKit, when } from '@ohos/hypium'; 981 982class ClassName { 983 constructor() { 984 } 985 986 async method_1(arg: string) { 987 return new Promise<string>((resolve: Function, reject: Function) => { 988 setTimeout(() => { 989 console.log('执行'); 990 resolve('数据传递'); 991 }, 2000); 992 }); 993 } 994} 995export default function mockPromiseTest() { 996 describe('mockPromiseTest', () => { 997 it('testMockfunc', 0, async (done: Function) => { 998 console.info("it1 begin"); 999 // 1.创建一个mock能力的对象MockKit 1000 let mocker: MockKit = new MockKit(); 1001 // 2.创建一个对象claser 1002 let claser: ClassName = new ClassName(); 1003 // 3.进行mock操作,比如需要对ClassName类的method_1函数进行mock 1004 let mockfunc: Function = mocker.mockFunc(claser, claser.method_1); 1005 // 4.期望claser.method_1函数被mock后, 以'test'为参数调用函数时返回一个promise对象 1006 when(mockfunc)('test').afterReturn(new Promise<string>((resolve: Function, reject: Function) => { 1007 console.log("do something"); 1008 resolve('success something'); 1009 })); 1010 // 5.执行mock后的函数,即对定义的promise进行后续执行 1011 let result = await claser.method_1('test'); 1012 expect(result).assertEqual("success something"); 1013 done(); 1014 }) 1015 }) 1016} 1017``` 1018 1019**示例11:verify times函数的使用(验证函数调用次数)** 1020 1021```javascript 1022import { describe, it, MockKit, when } from '@ohos/hypium' 1023 1024class ClassName { 1025 constructor() { 1026 } 1027 1028 method_1(...arg: string[]) { 1029 return '888888'; 1030 } 1031} 1032export default function verifyTimesTest() { 1033 describe('verifyTimesTest', () => { 1034 it('test_verify_times', 0, () => { 1035 // 1.创建MockKit对象 1036 let mocker: MockKit = new MockKit(); 1037 // 2.创建类对象 1038 let claser: ClassName = new ClassName(); 1039 // 3.mock 类ClassName对象的某个方法,比如method_1 1040 let func_1: Function = mocker.mockFunc(claser, claser.method_1); 1041 // 4.期望被mock后的函数返回结果'4' 1042 when(func_1)('123').afterReturn('4'); 1043 // 5.随机执行几次函数,参数如下 1044 claser.method_1('123', 'ppp'); 1045 claser.method_1('abc'); 1046 claser.method_1('xyz'); 1047 claser.method_1(); 1048 claser.method_1('abc', 'xxx', 'yyy'); 1049 claser.method_1('abc'); 1050 claser.method_1(); 1051 // 6.验证函数method_1且参数为'abc'时,执行过的次数是否为2 1052 mocker.verify('method_1', ['abc']).times(2); 1053 }) 1054 }) 1055} 1056``` 1057 1058 1059**示例12:verify atLeast函数的使用(验证函数调用次数)** 1060 1061```javascript 1062import { describe, it, MockKit, when } from '@ohos/hypium' 1063 1064class ClassName { 1065 constructor() { 1066 } 1067 1068 method_1(...arg: string[]) { 1069 return '888888'; 1070 } 1071} 1072export default function verifyAtLeastTest() { 1073 describe('verifyAtLeastTest', () => { 1074 it('test_verify_atLeast', 0, () => { 1075 // 1.创建MockKit对象 1076 let mocker: MockKit = new MockKit(); 1077 // 2.创建类对象 1078 let claser: ClassName = new ClassName(); 1079 // 3.mock 类ClassName对象的某个方法,比如method_1 1080 let func_1: Function = mocker.mockFunc(claser, claser.method_1); 1081 // 4.期望被mock后的函数返回结果'4' 1082 when(func_1)('123').afterReturn('4'); 1083 // 5.随机执行几次函数,参数如下 1084 claser.method_1('123', 'ppp'); 1085 claser.method_1('abc'); 1086 claser.method_1('xyz'); 1087 claser.method_1(); 1088 claser.method_1('abc', 'xxx', 'yyy'); 1089 claser.method_1(); 1090 // 6.验证函数method_1且参数为空时,是否至少执行过2次 1091 mocker.verify('method_1', []).atLeast(2); 1092 }) 1093 }) 1094} 1095``` 1096 1097**示例13:mock静态函数** 1098> @since1.0.16 支持 1099 1100```javascript 1101import { describe, it, expect, MockKit, when, ArgumentMatchers } from '@ohos/hypium'; 1102 1103class ClassName { 1104 constructor() { 1105 } 1106 1107 static method_1() { 1108 return 'ClassName_method_1_call'; 1109 } 1110} 1111 1112export default function staticTest() { 1113 describe('staticTest', () => { 1114 it('staticTest_001', 0, () => { 1115 let really_result = ClassName.method_1(); 1116 expect(really_result).assertEqual('ClassName_method_1_call'); 1117 // 1.创建MockKit对象 1118 let mocker: MockKit = new MockKit(); 1119 // 2.mock 类ClassName对象的某个方法,比如method_1 1120 let func_1: Function = mocker.mockFunc(ClassName, ClassName.method_1); 1121 // 3.期望被mock后的函数返回结果'mock_data' 1122 when(func_1)(ArgumentMatchers.any).afterReturn('mock_data'); 1123 let mock_result = ClassName.method_1(); 1124 expect(mock_result).assertEqual('mock_data'); 1125 // 清除mock能力 1126 mocker.clear(ClassName); 1127 let really_result1 = ClassName.method_1(); 1128 expect(really_result1).assertEqual('ClassName_method_1_call'); 1129 }) 1130 }) 1131} 1132``` 1133 1134#### 数据驱动 1135 1136##### 约束限制 1137 1138单元测试框架数据驱动能力从[框架 1.0.2版本](https://ohpm.openharmony.cn/#/cn/detail/@ohos%2Fhypium)开始支持。 1139 1140- 数据参数传递 : 为指定测试套、测试用例传递测试输入数据参数。 1141- 压力测试 : 为指定测试套、测试用例设置执行次数。 1142 1143数据驱动可以根据配置参数来驱动测试用例的执行次数和每一次传入的参数,使用时依赖data.json配置文件,文件内容如下: 1144 1145>说明 : data.json与测试用例*.test.js|ets文件同目录 1146 1147```json 1148{ 1149 "suites": [{ 1150 "describe": ["actsAbilityTest"], 1151 "stress": 2, 1152 "params": { 1153 "suiteParams1": "suiteParams001", 1154 "suiteParams2": "suiteParams002" 1155 }, 1156 "items": [{ 1157 "it": "testDataDriverAsync", 1158 "stress": 2, 1159 "params": [{ 1160 "name": "tom", 1161 "value": 5 1162 }, { 1163 "name": "jerry", 1164 "value": 4 1165 }] 1166 }, { 1167 "it": "testDataDriver", 1168 "stress": 3 1169 }] 1170 }] 1171} 1172``` 1173 1174配置参数说明: 1175 1176| | 配置项名称 | 功能 | 必填 | 1177| :--- | :--------- | :------------------------------------ | ---- | 1178| 1 | "suite" | 测试套配置 。 | 是 | 1179| 2 | "items" | 测试用例配置 。 | 是 | 1180| 3 | "describe" | 测试套名称 。 | 是 | 1181| 4 | "it" | 测试用例名称 。 | 是 | 1182| 5 | "params" | 测试套 / 测试用例 可传入使用的参数 。 | 否 | 1183| 6 | "stress" | 测试套 / 测试用例 指定执行次数 。 | 否 | 1184 1185示例代码: 1186 1187DevEco Studio从V3.0 Release(2022-09-06)版本开始支持 1188 1189stage模型: 1190 1191在TestAbility目录下TestAbility.ets文件中导入data.json,并在Hypium.hypiumTest() 方法执行前,设置参数数据 1192 1193FA模型: 1194 1195在TestAbility目录下app.js或app.ets文件中导入data.json,并在Hypium.hypiumTest() 方法执行前,设置参数数据 1196 1197```javascript 1198import AbilityDelegatorRegistry from '@ohos.application.abilityDelegatorRegistry' 1199import { Hypium } from '@ohos/hypium' 1200import testsuite from '../test/List.test' 1201import data from '../test/data.json'; 1202 1203... 1204Hypium.setData(data); 1205Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite); 1206... 1207``` 1208 1209```javascript 1210 import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; 1211 1212 export default function abilityTest() { 1213 describe('actsAbilityTest', () => { 1214 it('testDataDriverAsync', 0, async (done: Function, data: ParmObj) => { 1215 console.info('name: ' + data.name); 1216 console.info('value: ' + data.value); 1217 done(); 1218 }); 1219 1220 it('testDataDriver', 0, () => { 1221 console.info('stress test'); 1222 }) 1223 }) 1224} 1225 interface ParmObj { 1226 name: string, 1227 value: string 1228 } 1229``` 1230>说明 : 若要使用数据驱动传入参数功能,测试用例it的func必须传入两个参数:done定义在前面,data定义在后面;若不使用数据驱动传入参数功能,func可以不传参或传入done 1231 1232正确示例: 1233```javascript 1234 it('testcase01', 0, async (done: Function, data: ParmObj) => { 1235 ... 1236 done(); 1237 }); 1238 it('testcase02', 0, async (done: Function) => { 1239 ... 1240 done(); 1241 }); 1242 it('testcase03', 0, () => { 1243 ... 1244 }); 1245``` 1246错误示例: 1247```javascript 1248 it('testcase01', 0, async (data: ParmObj, done: Function) => { 1249 ... 1250 done(); 1251 }); 1252 it('testcase02', 0, async (data: ParmObj) => { 1253 ... 1254 }); 1255``` 1256 1257#### 专项能力 1258 1259该项能力需通过在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) 1260 1261- **筛选能力** 1262 1263 1、按测试用例属性筛选 1264 1265 可以利用框架提供的Level、Size、TestType 对象,对测试用例进行标记,以区分测试用例的级别、粒度、测试类型,各字段含义及代码如下: 1266 1267 | Key | 含义说明 | Value取值范围 | 1268 | -------- | ------------ |----------------------------------------------------------------------------------------------------------------------------------------------------| 1269 | level | 用例级别 | "0","1","2","3","4", 例如:-s level 1 | 1270 | size | 用例粒度 | "small","medium","large", 例如:-s size small | 1271 | testType | 用例测试类型 | "function","performance","power","reliability","security","global","compatibility","user","standard","safety","resilience", 例如:-s testType function | 1272 1273 示例代码: 1274 1275 ```javascript 1276 import { describe, it, expect, TestType, Size, Level } from '@ohos/hypium'; 1277 1278 export default function attributeTest() { 1279 describe('attributeTest', () => { 1280 it("testAttributeIt", TestType.FUNCTION | Size.SMALLTEST | Level.LEVEL0, () => { 1281 console.info('Hello Test'); 1282 }) 1283 }) 1284} 1285 ``` 1286 1287 示例命令: 1288 1289 ```shell 1290 hdc shell aa test -b xxx -m xxx -s unittest OpenHarmonyTestRunner -s testType function -s size small -s level 0 1291 ``` 1292 1293 该命令作用是筛选测试应用中同时满足,用例测试类型是“function”、用例粒度是“small”、用例级别是“0”的三个条件用例执行。 1294 1295 2、按测试套/测试用例名称筛选 1296 1297 注意:测试套和测试用例的命名要符合框架规则,即以字母开头,后跟一个或多个字母、数字,不能包含特殊符号。 1298 1299 框架可以通过指定测试套与测试用例名称,来指定特定用例的执行,测试套与用例名称用“#”号连接,多个用“,”英文逗号分隔 1300 1301 | Key | 含义说明 | Value取值范围 | 1302 | -------- | ----------------------- | ------------------------------------------------------------ | 1303 | class | 指定要执行的测试套&用例 | ${describeName}#${itName},${describeName} , 例如:-s class attributeTest#testAttributeIt | 1304 | notClass | 指定不执行的测试套&用例 | ${describeName}#${itName},${describeName} , 例如:-s notClass attributeTest#testAttribut | 1305 1306 示例代码: 1307 1308 ```javascript 1309 import { describe, it, expect, TestType, Size, Level } from '@ohos/hypium'; 1310 1311 export default function attributeTest() { 1312 describe('describeTest_000', () => { 1313 it("testIt_00", TestType.FUNCTION | Size.SMALLTEST | Level.LEVEL0, () => { 1314 console.info('Hello Test'); 1315 }) 1316 1317 it("testIt_01", TestType.FUNCTION | Size.SMALLTEST | Level.LEVEL0, () => { 1318 console.info('Hello Test'); 1319 }) 1320 }) 1321 1322 describe('describeTest_001', () => { 1323 it("testIt_02", TestType.FUNCTION | Size.SMALLTEST | Level.LEVEL0, () => { 1324 console.info('Hello Test'); 1325 }) 1326 }) 1327} 1328 ``` 1329 1330 示例命令1: 1331 1332 ```shell 1333 hdc shell aa test -b xxx -m xxx -s unittest OpenHarmonyTestRunner -s class describeTest_000#testIt_00,describeTest_001 1334 ``` 1335 1336 该命令作用是执行“describeTest_001”测试套中所有用例,以及“describeTest_000”测试套中的“testIt_00”用例。 1337 1338 示例命令2: 1339 1340 ```shell 1341 hdc shell aa test -b xxx -m xxx -s unittest OpenHarmonyTestRunner -s notClass describeTest_000#testIt_01 1342 ``` 1343 1344 该命令作用是不执行“describeTest_000”测试套中的“testIt_01”用例。 1345 1346- **随机执行** 1347 1348 使测试套与测试用例随机执行,用于稳定性测试。 1349 1350 | Key | 含义说明 | Value取值范围 | 1351 | ------ | ------------------------------------ | ---------------------------------------------- | 1352 | random | @since1.0.3 测试套、测试用例随机执行 | true, 不传参默认为false, 例如:-s random true | 1353 1354 示例命令: 1355 1356 ```shell 1357 hdc shell aa test -b xxx -m xxx -s unittest OpenHarmonyTestRunner -s random true 1358 ``` 1359 1360- **压力测试** 1361 1362 指定要执行用例的执行次数,用于压力测试。 1363 1364 | Key | 含义说明 | Value取值范围 | 1365 | ------ | ------------------------------------ | ------------------------------ | 1366 | stress | @since1.0.5 指定要执行用例的执行次数 | 正整数, 例如: -s stress 1000 | 1367 1368 示例命令: 1369 1370 ```shell 1371 hdc shell aa test -b xxx -m xxx -s unittest OpenHarmonyTestRunner -s stress 1000 1372 ``` 1373 1374- **用例超时时间设置** 1375 1376 指定测试用例执行的超时时间,用例实际耗时如果大于超时时间,用例会抛出"timeout"异常,用例结果会显示“excute timeout XXX” 1377 1378 | Key | 含义说明 | Value取值范围 | 1379 | ------- | -------------------------- | ---------------------------------------------------- | 1380 | timeout | 指定测试用例执行的超时时间 | 正整数(单位ms),默认为 5000,例如: -s timeout 15000 | 1381 1382 示例命令: 1383 1384 ```shell 1385 hdc shell aa test -b xxx -m xxx -s unittest OpenHarmonyTestRunner -s timeout 15000 1386 ``` 1387 1388- **遇错即停模式** 1389 1390 | Key | 含义说明 | Value取值范围 | 1391 | ------------ | ------------------------------------------------------------ | ---------------------------------------------------- | 1392 | breakOnError | @since1.0.6 遇错即停模式,当执行用例断言失败或者发生错误时,退出测试执行流程 | true, 不传参默认为false, 例如:-s breakOnError true | 1393 1394 示例命令: 1395 1396 ```shell 1397 hdc shell aa test -b xxx -m xxx -s unittest OpenHarmonyTestRunner -s breakOnError true 1398 ``` 1399 1400- **测试套中用例信息输出** 1401 1402 输出测试应用中待执行的测试用例信息 1403 1404 | Key | 含义说明 | Value取值范围 | 1405 | ------ | ---------------------------- | ---------------------------------------------- | 1406 | dryRun | 显示待执行的测试用例信息全集 | true, 不传参默认为false, 例如:-s dryRun true | 1407 1408 示例命令: 1409 1410 ```shell 1411 hdc shell aa test -b xxx -m xxx -s unittest OpenHarmonyTestRunner -s dryRun true 1412 ``` 1413 1414- **嵌套能力** 1415 1416 1.示例代码 1417 ```javascript 1418 // Test1.test.ets 1419 import { describe, expect, it } from '@ohos/hypium'; 1420 import test2 from './Test2.test'; 1421 1422 export default function test1() { 1423 describe('test1', () => { 1424 it('assertContain1', 0, () => { 1425 let a = true; 1426 let b = true; 1427 expect(a).assertEqual(b); 1428 }) 1429 // 引入测试套test2 1430 test2(); 1431 }) 1432 } 1433 ``` 1434 1435 ```javascript 1436 //Test2.test.ets 1437 import { describe, expect, it } from '@ohos/hypium'; 1438 1439 export default function test2() { 1440 describe('test2', () => { 1441 it('assertContain1', 0, () => { 1442 let a = true; 1443 let b = true; 1444 expect(a).assertEqual(b); 1445 }) 1446 it('assertContain2', 0, () => { 1447 let a = true; 1448 let b = true; 1449 expect(a).assertEqual(b); 1450 }) 1451 }) 1452 } 1453 ``` 1454 1455 ```javascript 1456 //List.test.ets 1457 import test1 from './nest/Test1.test'; 1458 1459 export default function testsuite() { 1460 test1(); 1461 } 1462 ``` 1463 1464 2.示例筛选参数 1465 ```shell 1466 #执行test1的全部测试用例 1467 -s class test1 1468 ``` 1469 ```shell 1470 #执行test1的子测试用例 1471 -s class test1#assertContain1 1472 ``` 1473 ```shell 1474 #执行test1的子测试套test2的测试用例 1475 -s class test1.test2#assertContain1 1476 ``` 1477 1478- **跳过能力** 1479 1480 | Key | 含义说明 | Value取值范围 | 1481 | ------------ | ------------------------------------------------------------ | ---------------------------------------------------- | 1482 | skipMessage | @since1.0.17 显示待执行的测试用例信息全集中是否包含跳过测试套和跳过用例的信息 | true/false, 不传参默认为false, 例如:-s skipMessage true | 1483 | runSkipped | @since1.0.17 指定要执行的跳过测试套&跳过用例 | all,skipped,${describeName}#${itName},${describeName},不传参默认为空,例如:-s runSkipped all | 1484 1485 1.示例代码 1486 1487 ```javascript 1488 //Skip1.test.ets 1489 import { expect, xdescribe, xit } from '@ohos/hypium'; 1490 1491 export default function skip1() { 1492 xdescribe('skip1', () => { 1493 //注意:在xdescribe中不支持编写it用例 1494 xit('assertContain1', 0, () => { 1495 let a = true; 1496 let b = true; 1497 expect(a).assertEqual(b); 1498 }) 1499 }) 1500 } 1501 ``` 1502 1503 ```javascript 1504 //Skip2.test.ets 1505 import { describe, expect, xit, it } from '@ohos/hypium'; 1506 1507 export default function skip2() { 1508 describe('skip2', () => { 1509 //默认会跳过assertContain1 1510 xit('assertContain1', 0, () => { 1511 let a = true; 1512 let b = true; 1513 expect(a).assertEqual(b); 1514 }) 1515 it('assertContain2', 0, () => { 1516 let a = true; 1517 let b = true; 1518 expect(a).assertEqual(b); 1519 }) 1520 }) 1521 } 1522 ``` 1523 1524 1525 1526### 使用方式 1527 1528单元测试框架以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)。 1529>**说明** 1530> 1531>1.0.8版本开始单元测试框架以HAR(Harmony Archive)格式发布 1532> 1533> 1534 1535## Ui测试框架功能特性 1536 1537| No. | 特性 | 功能说明 | 1538| ---- |-----------|-------------------------------------------------| 1539| 1 | Driver | Ui测试的入口,提供查找控件,检查控件存在性以及注入按键能力。 | 1540| 2 | On | 用于描述目标控件特征(文本、id、类型等),`Driver`根据`On`描述的控件特征信息来查找控件。 | 1541| 3 | Component | Driver查找返回的控件对象,提供查询控件属性,滑动查找等触控和检视能力。 | 1542| 4 | UiWindow | Driver查找返回的窗口对象,提供获取窗口属性、操作窗口的能力。 | 1543 1544**使用者在测试脚本通过如下方式引入使用:** 1545 1546```typescript 1547import {Driver,ON,Component,UiWindow,MatchPattern} from '@ohos.UiTest' 1548``` 1549 1550> 须知 1551> 1. `On`类提供的接口全部是同步接口,使用者可以使用`builder`模式链式调用其接口构造控件筛选条件。 1552> 2. `Driver`和`Component`类提供的接口全部是异步接口(`Promise`形式),**需使用`await`语法**。 1553> 3. Ui测试用例均需使用**异步**语法编写用例,需遵循单元测试框架异步用例编写规范。 1554 1555 1556 1557在测试用例文件中import `On/Driver/Component`类,然后调用API接口编写测试用例。 1558 1559```javascript 1560import { Driver, ON, Component } from '@kit.TestKit' 1561import { describe, it, expect } from '@ohos/hypium' 1562 1563export default function findComponentTest() { 1564 describe('findComponentTest', () => { 1565 it('uitest_demo0', 0, async () => { 1566 // create Driver 1567 let driver: Driver = Driver.create(); 1568 // find component by text 1569 let button: Component = await driver.findComponent(ON.text('Hello World').enabled(true)); 1570 // click component 1571 await button.click(); 1572 // get and assert component text 1573 let content: string = await button.getText(); 1574 expect(content).assertEqual('Hello World'); 1575 }) 1576 }) 1577} 1578``` 1579 1580### 基于ArkTS API的测试 1581 1582 1583#### Driver使用说明 1584 1585`Driver`类作为UiTest测试框架的总入口,提供查找控件,注入按键,单击坐标,滑动控件,手势操作,截图等能力。 1586 1587| No. | API | 功能描述 | 1588| ---- |-----------------------------------------------------------------| ---------------------- | 1589| 1 | create():Promise<Driver> | 静态方法,构造Driver。 | 1590| 2 | findComponent(on:On):Promise<Component> | 查找匹配控件。 | 1591| 3 | pressBack():Promise<void> | 单击BACK键。 | 1592| 4 | click(x:number, y:number):Promise<void> | 基于坐标点的单击。 | 1593| 5 | swipe(x1:number, y1:number, x2:number, y2:number):Promise<void> | 基于坐标点的滑动。 | 1594| 6 | assertComponentExist(on:On):Promise<void> | 断言匹配的控件存在。 | 1595| 7 | delayMs(t:number):Promise<void> | 延时。 | 1596| 8 | screenCap(s:path):Promise<void> | 截屏。 | 1597| 9 | findWindow(filter: WindowFilter): Promise<UiWindow> | 查找匹配窗口。 | 1598 1599其中assertComponentExist接口是断言API,用于断言当前界面存在目标控件;如果控件不存在,该API将抛出JS异常,使当前测试用例失败。 1600 1601```javascript 1602import { describe, it} from '@ohos/hypium'; 1603import { Driver, ON } from '@kit.TestKit'; 1604 1605export default function assertComponentExistTest() { 1606 describe('assertComponentExistTest', () => { 1607 it('Uitest_demo0', 0, async (done: Function) => { 1608 try{ 1609 // create Driver 1610 let driver: Driver = Driver.create(); 1611 // assert text 'hello' exists on current Ui 1612 await driver.assertComponentExist(ON.text('hello')); 1613 } finally { 1614 done(); 1615 } 1616 }) 1617 }) 1618} 1619``` 1620 1621`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)。 1622 1623#### On使用说明 1624 1625Ui测试框架通过`On`类提供了丰富的控件特征描述API,用来匹配查找要操作或检视的目标控件。`On`提供的API能力具有以下特点: 1626 1627- 支持匹配单属性和匹配多属性组合,例如同时指定目标控件text和id。 1628- 控件属性支持多种匹配模式(等于,包含,`STARTS_WITH`,`ENDS_WITH`,`REG_EXP`,`REG_EXP_ICASE`)。 1629 1630- 支持相对定位控件,可通过`isBefore`和`isAfter`等API限定邻近控件特征进行辅助定位。 1631 1632| No. | API | 功能描述 | 1633|-----|------------------------------------------|----------------------------| 1634| 1 | id(i:string):On | 指定控件id。 | 1635| 2 | text(t:string, p?:MatchPattern):On | 指定控件文本,可指定匹配模式。 | 1636| 3 | type(t:string):On | 指定控件类型。 | 1637| 4 | enabled(e:bool):On | 指定控件使能状态。 | 1638| 5 | clickable(c:bool):On | 指定控件可单击状态。 | 1639| 6 | longClickable(l:bool):On | 指定控件可长按状态。 | 1640| 7 | focused(f:bool):On | 指定控件获焦状态。 | 1641| 8 | scrollable(s:bool):On | 指定控件可滑动状态。 | 1642| 9 | selected(s:bool):On | 指定控件选中状态。 | 1643| 10 | checked(c:bool):On | 指定控件选择状态。 | 1644| 11 | checkable(c:bool):On | 指定控件可选择状态。 | 1645| 12 | isBefore(b:On):On | **相对定位**,限定目标控件位于指定特征控件之前。 | 1646| 13 | isAfter(b:On):On | **相对定位**,限定目标控件位于指定特征控件之后。 | 1647| 14 | id(i:string,p?:MatchPattern):On | 指定控件id,可指定匹配模式。 | 1648| 15 | hint(h:string, p?:MatchPattern):On | 指定控件提示文本,可指定匹配模式。 | 1649| 16 | type(t:string,p?:MatchPattern):On | 指定控件类型,可指定匹配模式。 | 1650| 17 | description(d:string,p?:MatchPattern):On | 指定控件描述文本信息,可指定匹配模式。 | 1651 1652其中,`text`,`id`,`type`,`hint`,`description`属性支持{`MatchPattern.EQUALS`,`MatchPattern.CONTAINS`,`MatchPattern.STARTS_WITH`,`MatchPattern.ENDS_WITH`,`MatchPattern.REG_EXP`,`MatchPattern.REG_EXP_ICASE`}六种匹配模式,缺省使用`MatchPattern.EQUALS`模式。 1653 1654`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)。 1655 1656##### 控件定位 1657 1658**示例代码1**:查找id是`Id_button`的控件。 1659 1660```javascript 1661let button: Component = await driver.findComponent(ON.id('Id_button')) 1662``` 1663 1664 **示例代码2**:查找id是`Id_button`并且状态是`enabled`的控件,适用于无法通过单一属性定位的场景。 1665 1666```javascript 1667let button: Component = await driver.findComponent(ON.id('Id_button').enabled(true)) 1668``` 1669 1670通过`On.id(x).enabled(y)`来指定目标控件的多个属性。 1671 1672**示例代码3**:查找文本中包含`hello`的控件,适用于不能完全确定控件属性取值的场景。 1673 1674```javascript 1675let txt: Component = await driver.findComponent(ON.text('hello', MatchPattern.CONTAINS)) 1676``` 1677 1678通过向`On.text()`方法传入第二个参数`MatchPattern.CONTAINS`来指定文本匹配规则;默认规则是`MatchPattern.EQUALS`,即目标控件text属性必须严格等于给定值。 1679 1680##### 控件相对定位 1681 1682**示例代码1**:查找位于文本控件`Item3_3`后面的,id是`Id_switch`的Switch控件。 1683 1684```javascript 1685let switch: Component = await driver.findComponent(ON.id('Id_switch').isAfter(ON.text('Item3_3'))) 1686``` 1687 1688通过`On.isAfter`方法,指定位于目标控件前面的特征控件属性,通过该特征控件进行相对定位。一般地,特征控件是某个具有全局唯一特征的控件(例如具有唯一的id或者唯一的text)。 1689 1690类似的,可以使用`On.isBefore`控件指定位于目标控件后面的特征控件属性,实现相对定位。 1691 1692#### Component使用说明 1693 1694`Component`类代表了Ui界面上的一个控件,一般是通过`Driver.findComponent(on)`方法查找到的。通过该类的实例,用户可以获取控件属性,单击控件,滑动查找,注入文本等操作。 1695 1696`Component`包含的常用API: 1697 1698| No. | API | 功能描述 | 1699|-----|------------------------------------|----------------------------| 1700| 1 | click():Promise<void> | 单击该控件。 | 1701| 2 | inputText(t:string):Promise<void> | 向控件中输入文本(适用于文本框控件)。 | 1702| 3 | scrollSearch(s:On):Promise<Component> | 在该控件上滑动查找目标控件(适用于List等控件)。 | 1703| 4 | scrollToTop(s:number):Promise<void> | 滑动到该控件顶部(适用于List等控件)。 | 1704| 5 | scrollTobottom(s:number):Promise<void> | 滑动到该控件底部(适用于List等控件)。 | 1705| 6 | getText():Promise<string> | 获取控件text。 | 1706| 7 | getId():Promise<number> | 获取控件id。 | 1707| 8 | getType():Promise<string> | 获取控件类型。 | 1708| 9 | isEnabled():Promise<bool> | 获取控件使能状态。 | 1709 1710`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)。 1711 1712**示例代码1**:单击控件。 1713 1714```javascript 1715let button: Component = await driver.findComponent(ON.id('Id_button')) 1716await button.click() 1717``` 1718 1719**示例代码2**:通过get接口获取控件属性后,可以使用单元测试框架提供的assert*接口做断言检查。 1720 1721```javascript 1722let component: Component = await driver.findComponent(ON.id('Id_title')) 1723expect(component !== null).assertTrue() 1724``` 1725 1726**示例代码3**:在List控件中滑动查找text是`Item3_3`的子控件。 1727 1728```javascript 1729let list: Component = await driver.findComponent(ON.id('Id_list')) 1730let found: Component = await list.scrollSearch(ON.text('Item3_3')) 1731expect(found).assertTrue() 1732``` 1733 1734**示例代码4**:向输入框控件中输入文本。 1735 1736```javascript 1737let editText: Component = await driver.findComponent(ON.type('InputText')) 1738await editText.inputText('user_name') 1739``` 1740#### UiWindow使用说明 1741 1742`UiWindow`类代表了Ui界面上的一个窗口,一般是通过`Driver.findWindow(WindowFilter)`方法查找到的。通过该类的实例,用户可以获取窗口属性,并进行窗口拖动、调整窗口大小等操作。 1743 1744`UiWindow`包含的常用API: 1745 1746| No. | API | 功能描述 | 1747| ---- | ------------------------------------------------------------ | -------------------------------------------------- | 1748| 1 | getBundleName(): Promise<string> | 获取窗口所属应用包名。 | 1749| 2 | getTitle(): Promise<string> | 获取窗口标题信息。 | 1750| 3 | focus(): Promise<bool> | 使得当前窗口获取焦点。 | 1751| 4 | moveTo(x: number, y: number): Promise<bool> | 将当前窗口移动到指定位置(适用于支持移动的窗口)。 | 1752| 5 | resize(wide: number, height: number, direction: ResizeDirection): Promise<bool> | 调整窗口大小(适用于支持调整大小的窗口)。 | 1753| 6 | split(): Promise<bool> | 将窗口模式切换为分屏模式(适用于支持分屏的窗口)。 | 1754| 7 | close(): Promise<bool> | 关闭当前窗口。 | 1755 1756`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)。 1757 1758**示例代码1**:获取窗口属性。 1759 1760```javascript 1761let window: UiWindow = await driver.findWindow({actived: true}) 1762let bundelName: string = await window.getBundleName() 1763``` 1764 1765**示例代码2**:移动窗口。 1766 1767```javascript 1768let window: UiWindow = await driver.findWindow({actived: true}) 1769await window.moveTo(500,500) 1770``` 1771 1772**示例代码3**:关闭窗口。 1773 1774```javascript 1775let window: UiWindow = await driver.findWindow({actived: true}) 1776await window.close() 1777``` 1778 1779#### 使用方式 1780 1781开发者可以下载Deveco Studio创建测试工程后,在其中调用框架提供接口进行相关测试操作,测试工程创建及测试脚本执行使用指南请参见[IDE指导文档](https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ohos-openharmony-test-framework-0000001267284568)。 1782UI测试框架使能需要执行如下命令。 1783>**说明** 1784> 1785>OpenHarmony 3.2版本需使用此命令,OpenHarmony4.0版本开始无需使用,默认使能。 1786> 1787> hdc shell param set persist.ace.testmode.enabled 1。 1788 1789### 基于shell命令测试 1790> 在开发过程中,若需要快速进行截屏、 录屏、注入UI模拟操作、获取控件树等操作,可以使用shell命令,更方便完成相应测试。 1791> 1792> **说明:** 1793> 1794> 使用cmd的方式,需要配置好hdc相关的环境变量。 1795 1796**命令列表** 1797 1798| 命令 | 配置参数 | 描述 | 1799|---------------|---------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 1800| help | | 显示uitest工具能够支持的命令信息。 | 1801| screenCap |[-p savePath] | 截屏。<br> **-p**:指定存储路径和文件名, 只支持存放在/data/local/tmp/下。<br> 默认存储路径:/data/local/tmp,文件名:时间戳 + .png 。 | 1802| dumpLayout |[-p savePath] <br/>[-w windowId] <br/>[-b bundleName]<br/>[-m true/false ]<br/>[-i ]<br/>[-a]| 获取当前的控件树。默认过滤不可见控件并进行窗口合并。<br> **-p** :指定存储路径和文件名, 只支持存放在/data/local/tmp/下。<br/> 默认存储路径:/data/local/tmp,文件名:时间戳 + .json。<br> **-w** :仅获取指定windowId的应用窗口。<br/> **-b** :仅获取指定bundleName的应用窗口。<br/> **-m** :是否进行窗口合并。true 代表进行合并,不使用该选项时默认进行窗口合并。<br/> **-i** :不过滤不可见控件,也不做窗口合并。<br/> **-a** :保存 BackgroundColor、 Content、FontColor、FontSize、extraAttrs 属性数据。<br/> **-a和-i** 不可同时使用。 | 1803| uiRecord | \<record/read> | 录制Ui操作。 <br> **record** :开始录制,将当前界面操作记录到/data/local/tmp/record.csv,结束录制操作使用Ctrl+C结束录制。 <br> **read** :读取并且打印录制数据。<br>各选项使用说明请参考[用户录制操作](#用户录制操作)。 | 1804| uiInput | \<help / click / doubleClick / longClick / fling / swipe / drag / dircFling / inputText / text / keyEvent> | 注入UI模拟操作。<br>各选项使用说明请参考[注入ui模拟操作](#注入ui模拟操作)。 | 1805| --version | | 获取当前工具版本号。 | 1806| start-daemon|\<token>| token:测试应用端生产的uitest链接标识。<br/>拉起uitest测试进程,与token指向的测试应用进行连接。<br/>由测试应用注入,不支持用户通过shell命令使用。 | 1807 1808 1809#### 截图使用示例 1810 1811```bash 1812# 存储路径:/data/local/tmp,文件名:时间戳 + .png。 1813hdc shell uitest screenCap 1814# 指定存储路径和文件名,存放在/data/local/tmp/下。 1815hdc shell uitest screenCap -p /data/local/tmp/1.png 1816``` 1817 1818#### 获取控件树使用示例 1819```bash 1820hdc shell uitest dumpLayout -p /data/local/tmp/1.json 1821``` 1822 1823#### 用户录制操作 1824>**说明** 1825> 1826> 录制过程中,需等待当前操作的识别结果在命令行输出后,再进行下一步操作。 1827```bash 1828# 将当前界面操作记录到/data/local/tmp/record.csv,结束录制操作使用Ctrl+C结束录制。 1829hdc shell uitest uiRecord record 1830# 读取并打印录制数据。 1831hdc shell uitest uiRecord read 1832``` 1833 1834#### 注入UI模拟操作 1835 1836| 命令 | 必填 | 描述 | 1837|------|------|-----------------| 1838| help | 是 | uiInput命令相关帮助信息。 | 1839| click | 是 | 模拟单击操作。 | 1840| doubleClick | 是 | 模拟双击操作。 | 1841| longClick | 是 | 模拟长按操作。 | 1842| fling | 是 | 模拟快滑操作。 | 1843| swipe | 是 | 模拟慢滑操作。 | 1844| drag | 是 | 模拟拖拽操作。 | 1845| dircFling | 是 | 模拟指定方向滑动操作。 | 1846| inputText | 是 | 指定坐标点,模拟输入框输入文本操作。 | 1847| text | 是 | 无需指定坐标点,在当前获焦处,模拟输入框输入文本操作。 | 1848| keyEvent | 是 | 模拟实体按键事件(如:键盘,电源键,返回上一级,返回桌面等),以及组合按键操作。 | 1849 1850##### uiInput click/doubleClick/longClick使用示例 1851 1852| 配置参数 | 必填 | 描述 | 1853|---------|------|-----------------| 1854| point_x | 是 | 点击x坐标点。 | 1855| point_y | 是 | 点击y坐标点。 | 1856 1857```shell 1858# 执行单击事件。 1859hdc shell uitest uiInput click 100 100 1860 1861# 执行双击事件。 1862hdc shell uitest uiInput doubleClick 100 100 1863 1864# 执行长按事件。 1865hdc shell uitest uiInput longClick 100 100 1866``` 1867 1868##### uiInput fling使用示例 1869 1870| 配置参数 | 必填 | 描述 | 1871|------|------------------|-----------------| 1872| from_x | 是 | 滑动起点x坐标。 | 1873| from_y | 是 | 滑动起点y坐标。 | 1874| to_x | 是 | 滑动终点x坐标。 | 1875| to_y | 是 | 滑动终点y坐标。 | 1876| swipeVelocityPps_ | 否 | 滑动速度,单位: (px/s),取值范围:200-40000。<br> 默认值: 600。 | 1877| stepLength_ | 否 | 滑动步长。默认值: 滑动距离/50。<br> **为实现更好的模拟效果,推荐参数缺省/使用默认值。** | 1878 1879 1880```shell 1881# 执行快滑操作,stepLength_缺省。 1882hdc shell uitest uiInput fling 10 10 200 200 500 1883``` 1884 1885##### uiInput swipe/drag使用示例 1886 1887| 配置参数 | 必填 | 描述 | 1888|------|------------------|-----------------| 1889| from_x | 是 | 滑动起点x坐标。 | 1890| from_y | 是 | 滑动起点y坐标。 | 1891| to_x | 是 | 滑动终点x坐标。 | 1892| to_y | 是 | 滑动终点y坐标。 | 1893| swipeVelocityPps_ | 否 | 滑动速度,单位: (px/s),取值范围:200-40000。<br> 默认值: 600。 | 1894 1895```shell 1896# 执行慢滑操作。 1897hdc shell uitest uiInput swipe 10 10 200 200 500 1898 1899# 执行拖拽操作。 1900hdc shell uitest uiInput drag 10 10 100 100 500 1901``` 1902 1903##### uiInput dircFling使用示例 1904 1905| 配置参数 | 必填 | 描述 | 1906|-------------------|-------------|----------| 1907| direction | 否 | 滑动方向,取值范围:[0,1,2,3],默认值为0。<br> 0代表向左滑动,1代表向右滑动,2代表向上滑动,3代表向下滑动。 | 1908| swipeVelocityPps_ | 否| 滑动速度,单位: (px/s),取值范围:200-40000。<br> 默认值: 600。 | 1909| stepLength | 否 | 滑动步长。<br> 默认值: 滑动距离/50。为更好的模拟效果,推荐参数缺省/使用默认值。 | 1910 1911```shell 1912# 执行左滑操作 1913hdc shell uitest uiInput dircFling 0 500 1914# 执行向右滑动操作 1915hdc shell uitest uiInput dircFling 1 600 1916# 执行向上滑动操作。 1917hdc shell uitest uiInput dircFling 2 1918# 执行向下滑动操作。 1919hdc shell uitest uiInput dircFling 3 1920``` 1921 1922##### uiInput inputText使用示例 1923 1924| 配置参数 | 必填 | 描述 | 1925|------|------------------|----------| 1926| point_x | 是 | 输入框x坐标点。 | 1927| point_y | 是 | 输入框y坐标点。 | 1928| text | 是 | 输入文本内容。 | 1929 1930```shell 1931# 执行输入框输入操作。 1932hdc shell uitest uiInput inputText 100 100 hello 1933``` 1934 1935##### uiInput text使用示例 1936| 配置参数 | 必填 | 描述 | 1937|------|------------------|----------| 1938| text | 是 | 输入文本内容。 | 1939 1940```shell 1941# 无需输入坐标点,在当前获焦处,执行输入框输入操作。若当前获焦处不支持文本输入,则无实际效果。 1942hdc shell uitest uiInput text hello 1943``` 1944 1945##### uiInput keyEvent使用示例 1946 1947| 配置参数 | 必填 | 描述 | 1948|------|------|----------| 1949| keyID1 | 是 | 实体按键对应ID,取值范围:KeyCode/Back/Home/Power。<br>当取Back/Home/Power时,不支持输入组合键。 | 1950| keyID2 | 否 | 实体按键对应ID。 | 1951| keyID3 | 否 | 实体按键对应ID。 | 1952 1953>**说明** 1954> 1955> 最多支持传入是三个键值,具体取值请参考[KeyCode](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis-input-kit/js-apis-keycode.md)。 1956 1957```shell 1958# 执行执行返回主页操作。 1959hdc shell uitest uiInput keyEvent Home 1960# 执行返回操作。 1961hdc shell uitest uiInput keyEvent Back 1962# 执行组合键粘贴操作。 1963hdc shell uitest uiInput keyEvent 2072 2038 1964``` 1965 1966#### 获取版本信息 1967 1968```bash 1969hdc shell uitest --version 1970``` 1971#### 拉起uitest测试进程 1972 1973```shell 1974hdc shell uitest start-daemon 1975``` 1976 1977### UI测试框架自构建方式 1978 1979#### 构建命令 1980 1981```shell 1982./build.sh --product-name rk3568 --build-target uitestkit 1983``` 1984#### 推送位置 1985 1986```shell 1987hdc target mount 1988hdc shell mount -o rw,remount / 1989hdc file send uitest /system/bin/uitest 1990hdc file send libuitest.z.so /system/lib/module/libuitest.z.so 1991hdc shell chmod +x /system/bin/uitest 1992``` 1993 1994## 版本信息 1995 1996| 版本号 | 功能说明 | 1997| :------ | :----------------------------------------------------------- | 1998| 3.2.2.1 | 1、增加抛滑、获取/设置屏幕方向接口<br />2、窗口处理逻辑增加不支持场景处理逻辑。 | 1999| 3.2.3.0 | 1、滑动控件进行滑动查找、滑动到尾部/顶部功能优化。 | 2000| 3.2.4.0 | 1、接口调用异常时会抛出错误码。 | 2001| 3.2.5.0 | 1、通信机制变更。 | 2002| 3.2.6.0 | 1、增加模拟鼠标操作能力接口<br />2、增加指定应用的窗口下查找目标控件接口。 | 2003| 4.0.1.1 | 1、支持在daemon运行时执行uitest dumpLayout。 | 2004| 4.0.1.2 | 1、模拟鼠标动作、键鼠协同功能优化。 | 2005| 4.0.1.3 | 1、示例代码更新<br />2、滑动控件进行滑动查找、滑动到尾部/顶部功能优化。 | 2006| 4.0.1.4 | 1、可选参数传入undefined时,当作默认值处理。 | 2007| 4.0.2.0 | 1、支持监听toast和dialog控件出现,使用callback的形式返回结果。 | 2008| 4.0.3.0 | 1、增加加载运行.abc文件机制。 | 2009| 4.0.4.0 | 1、支持abc_loader框架获取UI操作录制数据,屏幕数据,控件树等,并以callback的形式返回结果<br />2、修改录制数据结构。 | 2010| 4.1.1.1 | 1、对接批量获取控件信息能力,缩短获取控件信息的耗时。 | 2011| 4.1.2.0 | 1、增加shell命令方式注入UI模拟操作。 | 2012| 4.1.3.0 | 1、新增命令行功能,uitest dumuLayout -a ,dump信息中包含控件的背景色、字体颜色/大小信息。 | 2013| 4.1.4.0 | 1、dump信息中增加hint与description字段。<br />2、优化多指操作。<br />3、优化查找控件的效率。<br />4、uitest uiInput执行效率提升。 | 2014| 5.0.1.0 | 1、优化swipe操作。<br />2、inputText输入中文的实现方式改为设置剪贴板数据后,长按控件点击粘贴。 | 2015| 5.0.1.1 | 1、节点新增以下属性,背景色:backgroundColor,背景图片:backgroundImage,透明度:opacity,模糊度:blur,事件是否透传:hitTestBehavior 。 | 2016| 5.0.1.2 | 1、通过test Sa发布公共事件。<br />2、节点新增clip属性,判断其子节点是否进行切割。<br />3、过滤机制调整,节点只与其clip为true的父节点进行切换计算可见区域,可见区域宽/高小于等于0记为不可见。<br />4、调用inputText时,被输入字符串超过200个字符时,实现方式调整为设置剪贴板数据后,植入ctrl + v。 | 2017| 5.1.1.1 | 1、控件支持正则表达式方式进行查找 <br />2、获取控件属性中的提示文本信息。 <br />3、支持横向滑动查找操作。 <br />4、支持不指定坐标模拟输入文本的shell命令 hdc shell uitest uiInput text "xxxx"。 | 2018| 5.1.1.2 | uitest dumpLayout 能力增强<br /> -w:仅获取指定window id的应用窗口。<br/> -b :仅获取指定bundleName的应用窗口。<br/> -m :是否进行窗口合并。不使用该选项时默认进行窗口合并。 | 2019| 6.0.1.0 | 1、inputText支持追加输入和指定通过剪贴板粘贴输入。 <br/>2、开放鼠标原子事件。 <br/>3、新增模拟手表表冠旋转。 <br/>4、支持触控屏/鼠标注入长按操作和拖拽操作时指定长按时间。 | 2020 2021 2022## 白盒性能测试框架功能特性 2023 2024白盒性能测试框架PerfTest提供白盒性能测试能力,供开发者在测试场景使用,支持对指定代码段或指定场景的性能数据测试,支持自动化执行测试代码段,并采集耗时、CPU、内存、时延、帧率等性能数据。 2025 2026**使用者在测试脚本通过如下方式引入使用:** 2027 2028```typescript 2029import { PerfMetric, PerfTest, PerfTestStrategy, PerfMeasureResult } from '@kit.TestKit'; 2030``` 2031 2032### 接口说明 2033 2034`PerfTest`类作为白盒性能测试框架的入口,提供测试任务创建、测试代码段执行和数据采集、测量结果获取等能力。支持通过`PerfTestStrategy`设置测试执行策略,执行测试,并通过`PerfMeasureResult`获取测量结果。 2035 2036| No. | API | 功能描述 | 2037| ---- |-----------------------------------------------------------| ---------------------- | 2038| 1 | static create(strategy: PerfTestStrategy): PerfTest | 静态方法,构造一个PerfTest对象,并返回该对象。 | 2039| 2 | run(): Promise\<void> | 运行性能测试,迭代执行测试代码段并采集性能数据,使用Promise回调。 | 2040| 3 | getMeasureResult(metric: PerfMetric): PerfMeasureResult | 获取指定性能指标的测量数据。 | 2041| 4 | destroy(): void | 销毁PerfTest对象。 | 2042 2043`PerfTestStrategy`支持设置性能测试的执行策略,如测试性能指标、被测应用包名、迭代次数等。 2044 2045| 名称 | 类型 | 只读 | 可选 | 说明 | 2046| ---- | ------ | ---- | ---- |-----------| 2047| metrics | Array\<[PerfMetric](#perfmetric)> | 否 | 否 | 被测性能指标列表。 | 2048| actionCode | Callback\<Callback\<boolean>> | 否 | 否 | 测试代码段。 | 2049| resetCode | Callback\<Callback\<boolean>> | 否 | 是 | 测试结束环境重置代码段。默认为空,框架运行时不执行此代码段。 | 2050| bundleName | string | 否 | 是 | 被测应用包名。默认为"",框架运行时测试当前测试应用的性能数据。 | 2051| iterations | number | 否 | 是 | 测试迭代执行次数,默认值为5。 | 2052| timeout | number | 否 | 是 | 单次代码段(actionCode/resetCode)执行的超时时间,默认值为10000ms。 | 2053 2054`PerfMeasureResult`用于获取每轮测试的数据以及最大值、最小值、平均值等统计数据。 2055 2056| 名称 | 类型 | 只读 | 可选 | 说明 | 2057| ------ | ------ | ---- | ---- | ------------------------- | 2058| metric | [PerfMetric](#perfmetric) | 是 | 否 | 被测性能指标。 | 2059| roundValues | Array\<number> | 是 | 否 | 被测性能指标的各轮测量数据值。当数据采集失败时返回-1。 | 2060| maximum | number | 是 | 否 | 各轮测量数据最大值(剔除为-1的数据后计算)。 | 2061| minimum | number | 是 | 否 | 各轮测量数据最小值(剔除为-1的数据后计算)。 | 2062| average | number | 是 | 否 | 各轮测量数据平均值(剔除为-1的数据后计算)。 | 2063 2064### 使用示例 2065 2066白盒性能测试框架PerfTest支持基于代码段和针对指定场景的性能测试,使用示例如下。 2067 2068**示例1:基于代码段的性能测试,测试函数执行期间的应用性能** 2069 2070- 在main > ets > utils文件夹下新增PerfUtils.ets文件,在文件中编写自定义的函数。 2071 2072 ```ts 2073 export class PerfUtils { 2074 public static CalculateTest() { 2075 let num: number = 0 2076 for (let index = 0; index < 10000; index++) { 2077 num++; 2078 } 2079 } 2080 } 2081 ``` 2082 2083- 在ohosTest > ets > test文件夹下.test.ets文件中编写具体测试代码。 2084 2085 ```ts 2086 import { describe, it, expect } from '@ohos/hypium'; 2087 import { PerfMetric, PerfTest, PerfTestStrategy, PerfMeasureResult } from '@kit.TestKit'; 2088 import { PerfUtils } from '../../../main/ets/utils/PerfUtils'; 2089 2090 export default function PerfTestTest() { 2091 describe('PerfTestTest', () => { 2092 it('testExample0', 0, async (done: Function) => { 2093 let metrics: Array<PerfMetric> = [PerfMetric.DURATION, PerfMetric.CPU_USAGE] // 指定被测指标 2094 let actionCode = async (finish: Callback<boolean>) => { // 测试代码段中使用uitest进行列表滑动 2095 await await PerfUtils.CalculateTest() 2096 finish(true); 2097 }; 2098 let perfTestStrategy: PerfTestStrategy = { // 定义测试策略 2099 metrics: metrics, 2100 actionCode: actionCode, 2101 }; 2102 try { 2103 let perfTest: PerfTest = PerfTest.create(perfTestStrategy); // 创建测试任务对象PerfTest 2104 await perfTest.run(); // 执行测试,异步函数需使用await同步等待完成 2105 let res1: PerfMeasureResult = perfTest.getMeasureResult(PerfMetric.DURATION); // 获取耗时指标的测试结果 2106 let res2: PerfMeasureResult = perfTest.getMeasureResult(PerfMetric.CPU_USAGE); // 获取CPU使用率指标的测试结果 2107 perfTest.destroy(); // 销毁PerfTest对象 2108 expect(res1.average).assertLessOrEqual(1000); // 断言性能测试结果 2109 expect(res2.average).assertLessOrEqual(30); // 断言性能测试结果 2110 } catch (error) { 2111 console.error(`Failed to execute perftest. Cause:${JSON.stringify(error)}`); 2112 expect(false).assertTrue() 2113 } 2114 done(); 2115 }) 2116 }) 2117 } 2118 ``` 2119 2120**示例1:针对指定场景的性能测试,测试当前应用内列表滑动帧率** 2121 2122- 编写Index.ets页面代码,作为被测示例demo。 2123 2124 ```ts 2125 @Entry 2126 @Component 2127 struct ListPage { 2128 scroller: Scroller = new Scroller() 2129 private arr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 2130 build() { 2131 Row() { 2132 Column() { 2133 Scroll(this.scroller) { 2134 Column() { 2135 ForEach(this.arr, (item: number) => { 2136 Text(item.toString()) 2137 .width('90%') 2138 .height('40%') 2139 .fontSize(80) 2140 .textAlign(TextAlign.Center) 2141 .margin({ top: 10 }) 2142 }, (item: string) => item) 2143 } 2144 } 2145 .width('100%') 2146 .height('100%') 2147 .scrollable(ScrollDirection.Vertical) 2148 .scrollBar(BarState.On) 2149 .scrollBarColor(Color.Gray) 2150 } 2151 .width('100%') 2152 } 2153 .height('100%') 2154 } 2155 } 2156 ``` 2157 2158- 在ohosTest > ets > test文件夹下.test.ets文件中编写具体测试代码。 2159 2160 ```ts 2161 import { describe, it, expect } from '@ohos/hypium'; 2162 import { PerfMetric, PerfTest, PerfTestStrategy, PerfMeasureResult } from '@kit.TestKit'; 2163 import { abilityDelegatorRegistry, Driver, ON } from '@kit.TestKit'; 2164 import { Want } from '@kit.AbilityKit'; 2165 2166 const delegator: abilityDelegatorRegistry.AbilityDelegator = abilityDelegatorRegistry.getAbilityDelegator(); 2167 export default function PerfTestTest() { 2168 describe('PerfTestTest', () => { 2169 it('testExample',0, async (done: Function) => { 2170 let driver = Driver.create(); 2171 await driver.delayMs(1000); 2172 const bundleName = abilityDelegatorRegistry.getArguments().bundleName; 2173 const want: Want = { 2174 bundleName: bundleName, 2175 abilityName: 'EntryAbility' 2176 }; 2177 await delegator.startAbility(want); // 打开测试页面 2178 await driver.delayMs(1000); 2179 let scroll = await driver.findComponent(ON.type('Scroll')); 2180 await driver.delayMs(1000); 2181 let center = await scroll.getBoundsCenter(); // 获取Scroll可滚动组件坐标 2182 await driver.delayMs(1000); 2183 let metrics: Array<PerfMetric> = [PerfMetric.LIST_SWIPE_FPS] // 指定被测指标为列表滑动帧率 2184 let actionCode = async (finish: Callback<boolean>) => { // 测试代码段中使用uitest进行列表滑动 2185 await driver.fling({x: center.x, y: Math.floor(center.y * 3 / 2)}, {x: center.x, y: Math.floor(center.y / 2)}, 50, 20000); 2186 await driver.delayMs(3000); 2187 finish(true); 2188 }; 2189 let resetCode = async (finish: Callback<boolean>) => { // 复位环境,将列表划至顶部 2190 await scroll.scrollToTop(40000); 2191 await driver.delayMs(1000); 2192 finish(true); 2193 }; 2194 let perfTestStrategy: PerfTestStrategy = { // 定义测试策略 2195 metrics: metrics, 2196 actionCode: actionCode, 2197 resetCode: resetCode, 2198 iterations: 5, // 指定测试迭代次数 2199 timeout: 50000, // 指定actionCode和resetCode的超时时间 2200 }; 2201 try { 2202 let perfTest: PerfTest = PerfTest.create(perfTestStrategy); // 创建测试任务对象PerfTest 2203 await perfTest.run(); // 执行测试,异步函数需使用await同步等待完成 2204 let res: PerfMeasureResult = perfTest.getMeasureResult(PerfMetric.LIST_SWIPE_FPS); // 获取列表滑动帧率指标的测试结果 2205 perfTest.destroy(); // 销毁PerfTest对象 2206 expect(res.average).assertLargerOrEqual(60); // 断言性能测试结果 2207 } catch (error) { 2208 console.error(`Failed to execute perftest. Cause:${JSON.stringify(error)}`); 2209 } 2210 done(); 2211 }) 2212 }) 2213 } 2214 ``` 2215 2216PerfTest完整的API列表请参考[API文档](https://gitee.com/openharmony/interface_sdk-js/blob/master/api/@ohos.test.PerfTest.d.ts)及[示例文档说明](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis-test-kit/js-apis-perftest.md)。