发布于 2016-03-10 04:36:54 | 284 次阅读 | 评论: 0 | 来源: 分享
Xcode 编程开发软件
Xcode是苹果公司向开发人员提供的集成开发环境(非开源),用于开发Mac OS X,iOS的应用程序。其运行于苹果公司的Mac操作系统下。
在下面的章节,我会谈论两个框架之间最大的区别。
那么,我说干就干,开始为 ImagePickerSheetController 重写 UI 测试代码,我的开源项目之一。它本质上是在 iOS iMessage 选择图片的自定义操作表(action sheet)。
我第一个想重写的是一个非常基本的测试。简单地测试下当点击取消按钮时,控制器是否会撤回。
func testDismissal() {
let imageController = ImagePickerSheetController()
imageController.addAction(ImageAction(title: “Cancel”))
rootViewController.presentViewController(imageController, animated: true, completion: nil)
tester().tapViewWithAccessibilityLabel(“Cancel”)
tester().waitForAbsenceOfViewWithAccessibilityIdentifier(ID)
}
虽然这看起来非常简单,但这使用 XCTest 不可能实现。问题是,Xcode 的 UI 测试不允许访问实际的应用。API 提供的只是充当个代理。试图直接检索一个视图而不是使用 XCUIElement,这是行不通的,XCUIElement 只是用来展示视图。
在 WWDC 开发工具实验室有人告诉我,XCTest 的主要目标是让应用更容易测试。使用这个框架还是很明智的,于是我开始重写我的一个叫 Crimson 的应用测试。
Crimson 键盘显示了字母“E”的不同声调。
经过几个基本测试后,我开始对“声调视图”进行操作。它显示了一个特定字母的不同声调。我想测试的是下面两点:
我初以为不可能写这测试。和之前一样,你不能只通过审查实际的视图来检查属性。在我仔细查阅文档一两分钟后,我偶然发现 XCUIElement 的实用属性 value:
/*! The raw value attribute of the element. Depending on the element, the actual type can vary. */
var value: AnyObject? { get }
它所做的就是返回底层 UI 元素的 accessibilityValue。这让测试访问属性成为可能。所以,我需要唯一做的就是设置声调视图的 accessibilityValue:
class AccentView: UIView {
var selectedLetter: String {
didSet {
accessibilityValue = selectedLetter
}
}
}
直到我想检测颜色,这一切都进展顺利。由于 accessibilityValue 是字串类型而不是字典,它不可能再访问第二个属性。除此之外,accessibilityValue 就像是用户面对的 accessibilityIdentifier 一样。把 accessibilityValue 设置成模糊值,如一种颜色,是种非常不好的做法,而且完全违背了目的。
因此本次测试也是不可能实现的。虽然很容易检查元素的 frame 和 label 值,但它几乎不可能用来测试其他东西。
为了比较两种框架的性能,我为示例项目写了 3 份测试。为了模拟一个合理的测试组,每个测试都执行 20 遍,使得总共有 60 个测试。
对于第一次测量,我实现了文档中的测试。对于 XCTest,这意味着我会在每次测试后重新运行该应用。KIF 不支持这样,这让它在第一个比较中显得比较快速。
第二次测量通过返回导航而不是重新运行,重置了目标应用的状态。
第三次测量,我又取消了所有的动画。
因为 Xcode 7 不断崩溃,我不得不将迭代的次数减少到 5,然后将结果乘以 4。
正如我们在第一次比较中看到的,为每个规格重新运行应用,使得该测试组极其缓慢。令人惊讶的是 UI 测试模版建议使用这种重置方式,而不是通过导航,因为在大多情况下,没必要使用这种方式。
第二次比较显示,在正常情况下,XCTest 比 KIF 稍微快一点。需要注意的是,在一个比较大的且每天需要多次评估的测试组中,10 秒就可以省下大量的时间。
最后的比较中,XCTest 让人眼前一亮。然而,并不是每个项目都可以禁用所有动画。试想一个带许多自定义过渡效果的项目,测试组还是应确认过渡的正常运行。
对此的解决方案就是,把测试组分成不同运行环境的类,以控制应用的配置。
class UITests: XCTestCase {
private var launched = false
let app = XCUIApplication()
override func setUp() {
super.setUp()
continueAfterFailure = false
launchIfNecessary()
}
private func launchIfNecessary() {
if !launched {
launched = true
app.launchEnvironment = [“animations”: “0”]
app.launch()
}
}
func testDetail() { ... }
func testPopover() { ... }
func testActionSheet() { ... }
}
然后,AppDelegate 根据运行环境进行配置。
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if NSProcessInfo.processInfo().environment[“animations”] == “0” {
UIView.setAnimationsEnabled(false)
}
...
return true
}
}
这可以大大减少你等待测试通过的时间。
XCTest 可以让我们很容易编写简单的 UI 测试。而想要更高级的测试则是很困难甚至是不可能的。而 KIF 则更为灵活些。
然而,相比 KIF,XCTest 在查找 UI 元素上稍快些。在禁用动画的情况下,XCTest 是非常快。
虽然我希望 Xcode 内置的 UI 测试可以帮助我重写所有项目测试,但是我放弃了。我根本无法找到可以编写所有测试的方式。