Xcode 7 UI Testing 的抢先骗

本文翻译来源于:http://www.mokacoding.com/blog/xcode-7-ui-testing/ ,如有不妥之处请联系我进行删除,承诺本文不会用于商业用途。

注:以下代码都是基于 Xcode 7 Beta 1 和 Swift 2.0。我尽可能使用最新版本来实践,如果我有遗漏的地方,请在底部留言。

WWDC 2015 或许没给我们带来太大的惊喜,但它还是给我们带来一些非常棒的消息。我在另外篇文章 2015 iOS 的测试前景 中写道:

尽管 iOS、OS X 的单元测试在过去两年中变得越来越好,但其它可验收性的测试(UI 测试)却没有得到什么改善。

好了,可验收性测试(UI 测试)这种不利的情况在 Xcode 7 上得到很好的改善,而且变得更好。

Xcode Beta 7 增加了 “UI Testing Bundle”,通过它我们就可以写和运行可验收性(UI 测试)的测试了。

需要注意的是曾经风靡一时的 UIAutomation 已经被苹果官方移除,尽管它依然存活在 Instruments 里,但我们不在需要用到它了,我们将使用新的 API 写 UI 测试。

这些新的 API 对 Swift 和 XCTest 插件的支持非常友好,我们只需要一个简单的快捷键 ⌘U 即可运行已写好的 UI 测试。

新的测试跟我们以前写的原理差不多,例如我们使用 KIFCalabashAppium 等等写的一样,它能像人为一样操作 app,这得益于 iOS Accessibility 的交互协议,这些在 iOS 9 上都得到很大的提升。

开启 Xcode 7 UI 测试之旅

在开始尝试之前,我们要意识到尝鲜的代价,可能会有很多坑等着你来踩,所以我提醒你项目一定要使用 Xcode 7 和基于 iOS 9 进行开发。

在这篇文章中,我们会重写以前使用第三方 UI 测试架构写过的一个项目 Bench。如果你想了解更多如何写 UI 测试的话,可以查看这两篇 文章

在 Bench 项目中,首先需要新增一个 target,你会在 Xcode 7 上看到一个专门的测试项。

选择 UI Testing Bundle,这个支持 Object-C 和 Swift,我觉得最好使用 Swift 来写新的 API,如果你还没有,那么现在来尝试吧。

在生成的样板代码中还不能为我们做什么,它建议使用录制功能来生成 UI 测试代码。

Xcode 7 提供了 “录制” 这一有趣的功能,它能帮助我们生成大部分的 UI 测试代码。只需点击左下角的录制按钮,将游标移进任何一个 test....() 方法后,开始进行 App 的交互,即可看到 UI 测试代码如魔法般生成。

我们为 Bench 项目设定的第一个验收标准是:当我点击 “say hello” 按钮时,会看到一个弹出框。

此交互所生成的代码如下:

1
2
3
4
5
testSayHello() {
let app = XCUIApplication()
app.buttons["say hello"].tap()
app.alerts["Hello"].collectionViews.buttons["Dismiss"].tap()
}

正如你所看到的语法是非常简单的,当我们运行测试时,在控制台会看到如下信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Test Case '-[BenchUITests.BenchUITests testSayHello]' started.
2015-06-15 19:36:13.494 XCTRunner[3123:9611325] Continuing to run tests in the background with task ID 1
t = 1.60s Wait for app to idle
t = 1.81s Tap the "say hello" Button
t = 1.81s Wait for app to idle
t = 1.81s Find the "say hello" Button
t = 1.84s Dispatch the event
t = 2.08s Wait for app to idle
t = 2.10s Tap the "Dismiss" Button
t = 2.10s Wait for app to idle
t = 2.56s Find the "Dismiss" Button
t = 2.57s Dispatch the event
t = 2.81s Wait for app to idle
Test Case '-[BenchUITests.BenchUITests testSayHello]' passed (3.215 seconds).

这些输出信息记录了从开始测试到各种动作和花费的时间,这些对我们检查 app 的问题起到很大的作用,就像检测鸡蛋一样。

你也可以这样看测试报告,当你点击 inspector 按钮时会看到如下图一样的 app 测试失败状态页面。

你也许也注意到这测试跟人进行的测试没有什么不同之处。

App 状态的断言

Bench 的第二个测试标准是:当点击 “show elements” 按钮,会看到一个列表元素。

这个测试通过录制生成的代码如下:

1
2
3
4
5
6
7
8
func testShowElements() {
let app = XCUIApplication()
app.buttons["show elements"].tap()
app.tables.staticTexts["[N] Nitrogen (7)"].swipeUp()
app.tables.staticTexts["[Ir] Iridium (77)"].swipeUp()
app.tables.staticTexts["[Tl] Thallium (81)"].swipeUp()
app.tables.staticTexts["[Uut] Ununtrium (113)"].swipeUp()
}

这些对我们非常没有帮助,只是相当于列出一些简单的单元内容而已。

我们可以改这个 UI 测试为单纯的检验在屏幕上点击“show elements”时,只有唯一个 table 和 table 里有 118 行元素。除非在硬件设备取得重大突破,否则要展现 118 行元素还是有点慢的,这使得 UI 测试更具有确定性,不用依赖那些不确定因素如你的设计师或文案。

下面我们一步步重写这个测试,UI 测试的第一步都一样,点击 “show elements” 按钮加载下一个页面

1
let app = XCUIApplication()

XCUIApplication 是启动 app 的一个代理,通过这个来进行我们需要的交互和操作。

1
app.buttons["show elements"].tap()

.buttons["show elements"]XCUIApplication 实例后提供的一个 XCUIElementQuery 元素查询。我们通过此方法来查找出 “show elements” 这个按钮,如果不存这个名字或存在多个名称的按钮时,会查找失败。当我们通过此方法找到唯一的按钮时,会返回 XCUIElement 这个代理元素,我们就可以使用它的 tap() 方法来模拟点击事件。

XCUIApplication, XCUIElementQuery, XCUIElement 这三个 API 就可以完成一次简单 UI 测试,你也可以通过 Xcode 学习到更多的测试方式。

下一步就是我们要确定页面上只有一个 table。

1
XCTAssertEqual(app.tables.count, 1)

我们可以结合经常用的 XCTAssert... 到新的 UI 测试中。

确定只有一个 table 后,可以大胆将 assert 移到检查 table 有多少行元素上。

1
2
let table = app.tables.elementAtIndex(0)
XCTAssertEqual(table.cells.count, 118)

以上就是我们在 Xcode 7 上第一次完整的 UI 测试。

完整的测试应该是这样子的:

1
2
3
4
5
6
7
8
9
10
func testShowElements() {
let app = XCUIApplication()
app.buttons["show elements"].tap()

XCTAssertEqual(app.tables.count, 1)

let table = app.tables.elementAtIndex(0)
let expectedNumberOfElements: UInt = 118
XCTAssertEqual(table.cells.count, expectedNumberOfElements)
}

我自己对新的框架非常满意,将会不断使用这个框架来自动化测试 app,Apple 给出的信息也十分明确,他们十分重视这系列的 UI 测试,所以我们也应当成为 UI 测试的先行者,推动 UI 测试的发展。

下一步

首先,它会是不错的框架去尝试更复杂的场景,上面的测试标准我们设置的十分简单,只是教我们如何使用新的 UI 测试框架,而不是介绍它如何的强大。

在真实的测试环境中,经常会有网络、动画进入等等各种 app 启动状态和更复杂的使用场景。

另外有趣的是挖掘 UI 测试能否通过命令来启动和在 CI 环境下自动完成测试。

有些翻译不是很好,会继续修改…