iOS 9: Day by Day 第二天 UI测试

UI测试

本文翻译自Chris Grant《iOS9 Day-by-Day :: Day 2 :: UI Testing》(https://www.shinobicontrols.com/blog/ios9-day-by-day-day2-ui-testing)。感谢Chris Grant的辛苦工作!

自动化用户界面测试在开发应用程序的时候非常有用。它可以快速检测程序中的问题。iOS系统使用JavaScript编写的UIAutomation样例进行测试。整个过程涉及启动程序、Instruments以及创建和运行脚本。这个工作流程需要花一点时间才能适应。

UI测试

在Xcode 7中,苹果引入了一种新的方法来管理应用程序的UI测试。它允许我们查找和操作UI元素,并且检查它们的属性和状态。UI测试已经被完全整合到Xcode 7的测试报告中,并且与单元测试一起运行。Xcode 5整合的XCTest框架,在Xcode 7的时候被更新成支持UI测试。它支持在检查某个UI状态后执行断言。

辅助功能

为了进行UI测试,这个框架必须能够访问应用程序UI中的各个元素。这样它才能操作这些控件。我们可以定义手指头点击、滑动的位置。但这在不同尺寸的设备上没法正常工作。

这时辅助功能(无障碍环境)就有用了。辅助功能是苹果专门为残疾人与应用进行交互而维护的一个框架。它给UI界面提供了丰富的语义数据,以便残疾人通过这些辅助功能操作应用程序。iOS提供了越来越多的这种功能。虽然它们可能只能在某个应用里使用,但我们还是应该去改进这些API所使用的数据。在很多场景下这都是必要的,比如自定义控件的辅助功能没有自动识别出API的功能。

UI测试有能力通过辅助功能操作我们的应用,并且能够自动适应不同设备尺寸。当我们对界面进行重新布局时,不需要重写测试样例。辅助功能还能让残疾人更方便的使用我们的程序。

UI录制

一旦设置好可以访问的UI后,我们就可创建测试样例了。如果界面比较复杂的话,编写UI测试样例是一个费事、无聊的事情。不过在Xcode 7里,苹果引入了UI录制,它允许我们创建新测试、扩展已有测试。启动UI录制后,只要在设备或模拟器里与程序进行交互,就会自动生成测试代码。总体了解了UI测试后,下面实际动手试试。

创建UI测试样例

下面的例子里,我们使用最新的测试工具来创建测试样例。最终的示例代码可以在Github上找到。

设置

在Xcode 7中创建项目时,我们可以选择包含UI测试(include UI Tests)。它会自动帮我们创建一个UI测试目标,并设置好所有需要的内容。

这个示例非常简单,但包含了演示Xcode 7的UI测试功能所需要的内容。

这是一个菜单页面,它包含一个开关和按钮,其中按钮链接到详情页面。当开关处于关闭状态时,按钮被禁用。详情页面包含一个简单的按钮,点击后增加标签上显示的值。

使用UI录制

一旦设置好UI以及对应的功能。我们就可以开始编写UI测试样例来确保界面上的任何修改都不会影响程序的功能。

XCTest UI测试接口

在开始录制动作之前,需要确定判断条件。XCTest框架判断UI上元素的功能被分为下面三组API:

  • XCUIApplication:所测试应用的代理,允许启动应用程序作为测试目标。它总是启动一个新进程。这样做消耗的时间稍微多一点,但是能够保证测试环境不被干扰,处理的变量更少。
  • XCUIElement:程序中UI控件的代理。所有元素都拥有自己的类型和标识,用来查找程序中的任何一个元素。程序中的所有元素都被嵌套成一个树形结构。
  • XCUIElementQuery:用于查询元素。每个XCUIElement都有一个查询支撑。这类查询会遍历整个元素树,然后精确的匹配一个元素。如果没有匹配上,则测试失败。当我们检查一个元素是否在这棵树上时,有一个例外就是exists属性。这个属性用作判断的时候非常有用。我们可以使用更加通用的XCUIElementQuery进行查询。它会返回一个结果集。

了解了API后就可以开始写测试样例了。

测试1-确保开关关闭时不会发生导航

首先我们订一个一个函数用来包含测试代码。

func testTapViewDetailWhenSwitchIsOffDoesNothing() {

}

然后将光标移动到花括号中间,并且点击录制Xcode下方的按钮。

应用程序开始启动,点击开关将它关闭,然后点击“View Detail”按钮。在刚才的testTapViewDtailWhenSwitchIsOffDoesNothing会出现一下代码:

let app = XCUIApplication()
app.switches["View Detail Enabled Switch"].tap()
app.buttons["View Detail".tap()

再次点击录制按钮,停止录制。我们可以看到,应用程序并没有显示详情页面,但是测试样例并不知道这件事。我们必须加入判断语句,是否有内容发生改变。这可以通过检测导航条上的标题做到。这样做虽然不能满足所有条件,但都与目前的情况来说已经足够了。

XCTAssertEqual(app.navigationBars.element.identifier, "Menu")

重新运行测试,它还是会通过。我们将“Menu”字符串改为“Detail”,这时测试就会失败了。给测试样例添加一些注释:

func testTapViewDetailWhenSwitchIsOffDoesNothing() {
    let app = XCUIApplication()
    
    //关闭开关
    app.switches["View Detail Enabled Switch"].tap()
    
    //点击查看详情按钮
    app.buttons["View Detail"].tap()
    
    //验证是否还停留在菜单页
    XCTAssertEqual(app.navigaionBars.element.identifier, "Menu")
}

测试2-确保开关打开时导航条发生改变

第二个测试与第一个非常类似,因此我们不再详细介绍。唯一不同的地方是,这里的开关是打开的,因此应用程序应该会加载详情页面,并且XCTAssertEqual会确认这一点。

func testTapViewDetailWhenSwitchIsOnNavigatesToDetailViewController() {
    let app = XCUIApplication()
    
    //点击详情按钮
    app.buttons["View Detail"].tap()
    
    //验证导航条标识
    XCTAssertEqual(app.navigationBars.element.identifier, "Detail")
}

测试3-确保递增按钮正常工作

在这个测试中,我们确保用户点击递增按钮后,标签上的值会加1。测试代码的前两行非常类似,因此直接将它从上面复制下来就可以了。

let app = XCUIApplication()

//点击详情按钮,打开页面
app.buttons["View Detail"].tap()

下一步,获取按钮。需要点击这个按钮多次,因此先将它存储在一个变量中。我们还是可以进行录制。代码如下:

app.buttons["Increment Value"].tap()

停止录制,并且将代码改为:

let incrementButton = app.buttons["Increment Value"]

这样我们就不再需要手动写代码查找按钮。下面是查找标签的代码:

let valueLabel = app.staticTexts["Number Value Label"]

到目前为止,我们已经获取了所有感兴趣的控件。在这个测试里,我们将验证10次点击后,标签上的值是否正确。还是可以使用录制功能,点击十次按钮。不过之前已经将元素全部保存,因此只需要简单的输入一个循环就可以。

for index in 0...10 {
    //点击按钮
    incrementButton.tap()
    
    //确保每次增1
    XCTAssertEqual(valueLabel.value as! String, "\(index+1)")
}

这是整个测试包里的三个而已,但是给我们提供了非常好的切入点,并且很容易在这个上面进行扩充。为什么不尝试自己增加一些测试样例来验证当开关打开时按钮可以点击,关闭时不能点击?

录制出错

录制的时候,可能会发现当我们点击按钮后,并没有产生代码。这是因为交互的元素并不支持辅助功能。我们需要使用Xcode的“Accessibility Inspector”进行检查。

一旦辅助功能检查器被打开,按“Command+F7”,然后用鼠标覆盖一个元素。这时我们会看到光标下元素的详细信息。这会提示我们辅助功能到底能否找到该元素。

测试失败

如果测试失败,并且我们不知道为什么。这里有一些方法可以帮助我们。首先,访问Xcode的测试报告。

当我们打开这个视图,并且鼠标悬停在某个步骤时就会看到测试动作的右边有一个小眼睛一样的图标。如果点击这个眼睛,就可以看到该测试的详情信息。这样就可以可视化的检查UI状态,并且找出具体是哪里有问题。

与单元测试类似,我们可以给UI测试设置断点。这样就可以对行为进行调试,并且找出问题。当测试失败时,可以查看视图结构以及检查辅助功能的属性。

为什么需要UI测试

自动化UI测试能够极大的保障程序质量。在Xcode中进行测试非常简单,并且给程序增加辅助功能不只是帮助我们进行测试,还能提高对残疾人的友好度。

Xcode中的UI测试能够直接在持续集成服务器上运行。当测试失败时,我们可以通过Xcode机器人或者命令行方式立即获取信息。

扩展阅读

关于Xcode UI测试的更多信息,建议观看WWDC session 406:“UI Testing in Xcode”。或者查看文档:“Testing in Xcode Document”“Accessibility for Developers Documentation”

戴维营教育

戴维营教育(Dive In Education),潜心做IT职业教育!紧跟时代潮流,不弄虚作假!不忘初心!

推荐阅读更多精彩内容