iOS UI Testing 指北

iOS 业界有很多 UI 自动化测试框架,美团有一篇文章中对主流框架进行了对比,各大框架实现的思路有两种:基于苹果的 UI Testing(UI Automation)或者使用私有 API。这里只介绍苹果自家的 UI Testing,它是苹果自 Xcode 7 引入的 UI 自动化测试框架,有几个优势:

  • Xcode 自带,不需要搭建环境
  • 支持 OC、Swift,学习成本低
  • 支持 WebView 测试
  • 稳定性好

利用 UI Testing 可以对 app 的 UI 进行黑盒测试。目前还只能做比较简单的测试工作,基本原理是利用 iOS 的 Accessibility(原本是为帮助残障人士提供的框架)来查找 UI 上的元素并模拟点按、滚动等事件,配合对 UI 元素状态的校验来检查测试结果。

使用方法

要使用 UI Testing,只需要新建一个 UI Testing 的 target,Xcode 已经提供了现成的模板,非常方便。整个过程就三步:利用 XCUIApplicationXCUIElementQuery 查找元素、用 XCUIElement 模拟操作、用断言检查结果。

  • XCUIApplication

XCUIApplication 代表整个应用,可以用来启动、结束进程,或者传入一些启动参数,最常用的功能是利用 XCUIApplication 实例来查询 UI 上的元素。

1
2
3
XCUIApplication *app = [[XCUIApplication alloc] init];
[app launch];
[app terminate];
  • XCUIElementQuery

XCUIElementQuery 代表一系列的 UI 元素查询条件,可以级联使用。常用的使用方法如下:

1
2
3
4
5
6
7
8
// 查找所有的 collectionView 的 cell, collectionViews 和 cells 是 XCUIElementQuery 提供的方法
XCUIElementQuery *cells = app.collectionViews.cells;

// 使用 NSPredicate 为查询条件增加条件
XCUIElementQuery *cells = [app.collectionViews.cells matchingPredicate:[NSPredicate predicateWithFormat:@"identifier LIKE '?labelPrice?'"]];

// 使用下标来通过 accessibilityIdentifier 查询元素
XCUIElementQuery *cells = app.collectionViews.cells[@"some_id"];

通常一个页面中相同种类的 UI 元素很多,就需要使用 accessibilityIdentifier 来区分不同的元素。

  • XCUIElement

XCUIElement 代表具体的 UI 元素,比如一个 button 或者 scrollView。从 XCUIElementQuery 可以得到 XCUIElement

1
2
3
4
5
// 获取第一个 button
XCUIElement *firstButton = [buttons elementBoundByIndex:0];

// 等待 button 出现
[button waitForExistenceWithTimeout:3]

我们还可以通过 XCUIElement 的属性查询 UI 元素的状态,比如是否可点击,是否存在。得到 UI 元素调用 tapswipeUp 等方法就可以模拟操作了,使用起来非常方便。除了向指定元素模拟操作之外,还可以使用 XCUICoordinate 模拟基于屏幕坐标的事件。

UI Testing 是基于 XCTest 开发的,也就可以用 XCTest 提供的一系列断言用来检查结果,如 XCTAssertXCTAssertTrue 等。

常见问题

accessibilityIdentifier 没有设置

TBUIAutoTest 是一个自动生成 accessibilityIdentifier 的工具,原理是 hook UIViewaccessibilityIdentifieraccessibilityLabel 方法,通过运行时获取成员变量名来作为 accessibilityIdentifier

如何实现暂停 3 秒钟

测试代码是运行在另外一个进程中的,所以 sleep(3) 就好。

iOS 11 上向可见的元素发送事件报错,提示元素不可见

现象:执行 tap 操作时报 error: Error -25204 performing AXAction 2003 on element pid: 43616, elementOrHash.elementID: 4882574576.240,cell hittable 返回 NO
原因:isAccessibilityElement 方法默认返回 YES,在 iOS 11 上会出现返回 NO 的情况(同样代码 iOS 12 上正常)
解决:需要显示将该属性设置为 YES
SO:https://stackoverflow.com/questions/46819807/xcode-ui-testing-collection-view-cells-became-non-hittable-on-ios-11

DOMException Crash

现象:WebView 加载过程中 crash
原因:WebView 加载过程中查找 UI 元素会导致 crash
解决:

  1. 使用 WKWebView
  2. UIWebViewDelegate 中,webView 加载完成前调用 [self.view setAccessibilityElementsHidden:YES],加载完成后调用 [self.view setAccessibilityElementsHidden:NO]
  3. sleep 几秒等待 webView 加载完成 :]

被测试 app 如何判断正在进行 UI Test?

在启动 app 时增加一个启动参数,在 app 中读取。

1
2
3
4
5
6
7
8
9
10
// 测试代码
XCUIApplication *app = [[XCUIApplication alloc] init];
app.launchEnvironment = @{@"isUITest" : @YES};
[app launch];

// app 代码
+ (BOOL)isUITesting {
NSDictionary *environment = [[NSProcessInfo processInfo] environment];
return [environment[@"isUITest"] boolValue];
}
给鸡排饭加个蛋