新版本的 IDE Xcode7 带来了一个新的测试功能 — UITesting,利用它我们可以跑一些自动化的 UI 测试。


Xcode7 除了带来新功能之外,以前那个寂寂无名的 UIAutomation 也被剔除了,虽然它现在在 Instrument 里面仍然可用。但我们现在基本上可以完全抛弃它了,因为我们有新欢—UITesting,一套全新的API。


这套 API 有着友好的 Swift 接口跟 XCTest 插件,所以我们在启动测试的时候,只需要伸出两根手指,轻轻按下两个按键 ⌘U。

#####Xcode 7 UI testing 概览
如果是新建项目的话,Xcode已经为我们默认把 UITesting 的选项勾上了:


image

*默认勾选 UITesting*


如果要为已经存在的项目添加 UITesting,新建一个 target 就好了:


image

*常年孤独的 Unit Testing Bundle 终于有伴了*


在生成的 UITests.m 文件中,Xcode 已经为我们生成了一些默认的代码,但是它们目前不会产生什么实质性的作用。在代码注释中,Xcode 推荐我们:Use recording to get started writing UI tests.


Recording 是 Xcode7 提供的一项超级超级便捷的工具,可以为我们生成大部分的测试代码。首先我们将光标聚焦在 testExample 函数里面,然后点击下方小红点来开始录制,接着在 app 中进行一些交互,然后你将会见证奇迹发生的时刻:代码就这样被召唤出来了。


image


至于上例中,自动生成的代码反而由于编码问题被报错,我也有点醉。


image

*这是 Bug 吗*


将字符串里面的 \U 替换成 \u 就好了。


上面的例子中,测试通过的原则是:当我点击“戳我”按钮,弹出一个对话框。自动生成的代码如下:

1
2
3
4
5
6
- (void)testExample {
XCUIApplication *app = [[XCUIApplication alloc] init];
[app.buttons[@"\u6233\u6211"] tap];
[app.alerts.collectionViews.buttons[@"\u662f\u6211\u7684\u6233"] tap];

}

看起来非常直白,就是代码字面上的意思。点击函数左边的小按钮,我们可以看见类似下面终端输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Test Case '-[UITestDemoUITests testExample]' started.
t = 0.00s Start Test
t = 0.00s Set Up
t = 0.00s Launch pandara.UITestDemo
2015-10-23 18:11:42.685 XCTRunner[31796:964610] Continuing to run tests in the background with task ID 1
t = 1.00s Waiting for accessibility to load
t = 2.85s Wait for app to idle
t = 3.09s Tap "戳我" Button
t = 3.09s Wait for app to idle
t = 3.14s Find the "戳我" Button
t = 3.14s Snapshot accessibility hierarchy for pandara.UITestDemo
t = 3.18s Find: Descendants matching type Button
t = 3.19s Find: Elements matching predicate '"戳我" IN identifiers'
t = 3.19s Wait for app to idle
t = 3.25s Synthesize event
t = 3.54s Wait for app to idle
t = 4.02s Tap "是我的戳" Button
t = 4.02s Wait for app to idle
t = 4.07s Find the "是我的戳" Button
t = 4.07s Snapshot accessibility hierarchy for pandara.UITestDemo
t = 4.10s Find: Descendants matching type Alert
t = 4.11s Find: Descendants matching type CollectionView
t = 4.11s Find: Descendants matching type Button
t = 4.11s Find: Elements matching predicate '"是我的戳" IN identifiers'
t = 4.12s Wait for app to idle
t = 4.15s Check for UI interruption: Descendants matching type Alert
t = 4.16s Snapshot accessibility hierarchy for pandara.UITestDemo
t = 4.18s Find: Descendants matching type Alert
t = 4.19s Synthesize event
t = 4.45s Wait for app to idle

终端的输出显示了发生在 UI 上的操作,还有它们距离测试开始时的时间间隔。


在 Xcode 的 test reporter 中同样可以看到这些输出:


image


聪明的你可能已经察觉出了,UITesting 只能检测某个元素是否在屏幕上面,除此以外它不能检测出其他东西,例如是否多了个元素,是否布局有错乱。

#####判断 app 的状态
假设 app 存在这样的一种操作:点击按钮之后,弹出一个列表。那么这个时候测试通过的标准就是:点击按钮,弹出列表。利用 recording 生成的代码如下:


image


看起来好像还是没什么用。并且跟 Cell 的具体内容耦合得比较高。


我们可以简单地修改一下,来确保当按钮按下的时候,屏幕中有且仅有一个 table,并且 table 中确实有 30 个元素,除非你的屏幕坏了而导致一些元素不见了。相信这个新的测试会比产品经历跟设计师的信息稳定精准得多了去了。


下面,就让我们一步一步地来重写这个 test。test 的开始部分是一样,就是简单地点击屏幕上一开始显示的 show element 按钮。

1
XCUIApplication *app = [[XCUIApplication alloc] init];

XCUIApplication 是当前这个正在运行的 app 的代理,我们用它来跟 app 进行交互。

1
[app.buttons[@"show element"] tap];

.buttons[@"show element"] 是一个 XCUIElementQuery 对象,由XCUIApplication 生成。它代表一个名为 show elements 的按钮,当有且仅有一个匹配的 button,这条测试才会通过。当有一个按钮被匹配,这个查询就会返回该按钮的一个代理,一个XCUIElement 对象。通过这个代理的 tap 方法,我们可以点击这个 button。


XCUIApplication, XCUIElementQueryXCUIElement 是组成 UITesting 的三个重要的类。在它们的头文件中你可以查看更多信息。


然后,我们的下一步,就是要确保屏幕上显示的,有且仅有一个table:

1
XCTAssertEqual([app.tables count], 1);

当然用 XCTAssert 也是可以的。


在确保了我们有且仅有一个 table 之后,我们下一步就是要判断 table 里面的 cell 总数是否匹配我们的预期数目。

1
2
XCUIElement *table = [app.tables elementBoundByIndex:0];
XCTAssertEqual(table.cells.count, 30);

这就是我们的编写的第一个 UITest 啦~


最后整个测试看起来如下:

1
2
3
4
5
6
7
8
9
- (void)testExample {
XCUIApplication *app = [[XCUIApplication alloc] init];
[app.buttons[@"show element"] tap];

XCTAssertEqual([app.tables count], 1);

XCUIElement *table = [app.tables elementBoundByIndex:0];
XCTAssertEqual(table.cells.count, 30);
}

#####施主,你要到哪里去呀
虽然这个框架好像在很多情况下都不适用,例如难以确保页面内元素的布局是否正确,以及动态改变的 tableView 元素个数难以确定等等。但是这可以看做是苹果在 UITesting 迈出的第一步,期待以后会有发展。至少目前,把它当做一个玩具来玩也未曾不可。

以上大部分内容来自[mokacoding — Xcode 7 UI testing, a first look](Xcode 7 UI testing, a first look),有修改


以上