iOS 9 by Tutorials 笔记(十三)Testing

这几年 Apple 在 iOS 测试上改进不少,越来越简单快捷了:
  • Xcode 5 苹果推出了 XCTest 框架的第一个版本,相较上一版 SenTestingKit 增加了很多现代化的实现
  • Xcode 6 增加了异步 asynchronous 和性能 performance 测试
  • 今年随着 Xcode 7 面世 Apple 又推出了 code coverage reports(测试覆盖率报告)和 UI testing
关于测试我之前专门写过两篇文章详述,具体可以看这里:  Unit Testing for iOS Part ⅠUnit Testing for iOS Part Ⅱ
本章就挑摘要记录下,不做具体的深入了


Code coverage
开启代码覆盖率可以让你知道整个工程当前的测试情况,开启很简单:在Product\Scheme\Edit Scheme... 下选择 Test,勾选 Code Coverage — Gather coverage data 就 OK 了

运行测试,在 Xcode 左侧导航栏切换到 report navigator,点击 test action

切换到 Coverage tab,你就能看到测试覆盖率报告了

这个报告展示了基于当前文件的方法测试覆盖情况,你甚至可以进入单独的类中去查看单个方法被测试的次数

@testable imports and access control
通常我们在 test target 中要测试 model 的时候,都要先导入相应的 module,这是因为你的 app 和 test bundle 一般是分离的,但仅仅这样做还不够。
这涉及到了访问控制的概念,通常很多语言都会对从一个代码区块访问另一个代码区块做出限制, Swift 也不例外,在 Swift 中这种访问控制模式基于 modules 和 source files 的概念。
一个 module 是一个独立的代码分发单元,这可以是一个应用或一个框架,在本例子中,所有在  Workouts app 里的源代码是一个 module,而所有在 testing bundle 中代码是另外一个独立的 module;而一个 source file 是 module 中的一个 Swift 源代码文件,比如 Workout.swift
Swift 提供了三种等级的访问控制:
  • Public access 本权限下的实例允许任意 module 的代码访问
  • Internal access 本权限的实例仅允许同一 module 的代码访问
  • Private access 本权限的实例仅允许在当前 source file 中使用
默认的是 internal,所以想要从 test bundle 访问 app 中的实例是不可能的(因为跨了 module),全部设为 Public 又不现实,苹果审时度势在 Swift 2.0 推出了 @testable,可以让 internal 在 test bundle 中访问到
现在你只需要在 DataModelTests.swift 中将
  1. import Workouts

  2. 替换为:

  3. @testable import Workouts
复制代码
@testable 对 Private access 不起作用

UI testing
如果你的工程是用 Xcode 7 之前版本创建的,那么在写 UI test 之前先要添加 UItesting target

Run your first UI test
第一步将我们要测试的 View 先标记出来
  1. override func viewDidLoad() {  
  2.   super.viewDidLoad()
  3.   tableView.accessibilityIdentifier = "Workouts Table"
  4. }
复制代码


然后来写我们的 UI 测试方法
  1. func testRaysFullBodyWorkout() {  
  2.   let app = XCUIApplication()
  3.   // 1 得到所有的 table
  4.   let tableQuery = app.descendantsMatchingType(.Table)
  5.   // 2 找出之前标记为 "WorkoutsTable" 的 table
  6.   let workoutTable = tableQuery["Workouts Table"]
  7.   let cellQuery = workoutTable.childrenMatchingType(.Cell)
  8.   let identifier = "Ray's Full Body Workout"
  9.   let workoutQuery = cellQuery
  10.     .containingType(.StaticText, identifier: identifier)
  11.   let workoutCell = workoutQuery.element
  12.   workoutCell.tap()
  13.   // 3 模拟一些点按操作
  14.   let navBarQuery = app.descendantsMatchingType(.NavigationBar)
  15.   let navBar = navBarQuery[identifier]
  16.   let buttonQuery = navBar.descendantsMatchingType(.Button)
  17.   let backButton = buttonQuery["Workouts"]
  18.   backButton.tap()
  19. }
复制代码

当运行测试的时候,你会发现模拟器会自动启动并模拟整个操作过程。你也许又会问为什么没有 assertions,因为如果界面上某个 UI 元素不存在,测试就会失败。所以执行 UI test 其实暗含了 asserts
UI test classes
在 UI testing 中主要有这三种独立的类
  • XCUIApplication 表示代理当前 App
  • XCUIElement 表示代理当前 UI 元素
  • XCUIElementQuery 用做查询
    • descendantsMatchingType(_
    • childrenMatchingType(:_)
    • containingType(_

记住 XCUIApplication 和 XCUIElement 都仅仅是 proxies(代理人),并不是真正的 UI 对象

UI testing convenience methods
现在添加另一个测试,在具体某一项 Workout 详情页面,滚动到底部,点击 Select & Workout 按钮,此时会弹一个警告框,我们点 OK 来 dismiss 掉,最后返回到之前的 list 界面 同样是在 viewDidLoad() 中先标记 detail 页面的 table
  1. tableView.accessibilityIdentifier = "Workout Detail Table"  
复制代码
  1. func testRaysFullBodyWorkout() {  
  2.   let app = XCUIApplication()
  3.   //1 
  4.   let identifier = "Ray's Full Body Workout"
  5.   let workoutQuery = app.tables.cells
  6.     .containingType(.StaticText, identifier: identifier)
  7.   workoutQuery.element.tap()
  8.   //2
  9.   app.tables["Workout Detail Table"].swipeUp()
  10.   app.tables.buttons["Select & Workout"].tap()
  11.   app.alerts.buttons["OK"].tap()
  12.   //3
  13.   app.buttons["Workouts"].tap()
  14. }
复制代码
  • 获取到所有 cells,然后根据 identifier 筛选出来对应的 cell,点击进入详情页面
  • 向上滚动 table,找到 Select & Workout 按钮点击,弹出的对话框点 OK
  • 最后点击 navigation bar 上的 Workouts 按钮返回到 list 主页面

运行测试,失败了...

一般有三种方式向下查找到需要的 UI 元素
  • 如果你有一个唯一的标识可以使用下标,buttonsQuery["OK"]
  • 使用索引 tables.cells.elementAtIndex(0)
  • 如果能确保查询到只有一个元素,可以使用 XCUIElementQuery 的 element 属性
如果使用以上三种方法最后找到多个 XCUIElement,那么测试就会失败,这是因为 UI testing framework 不知道你到底想要与哪个 UI 元素进行交互。
而上面的测试失败是因为下面这行找到了两个以 Workouts 命名的按钮
  1. app.buttons["Workouts"]  
复制代码
注意黄圈圈出来的 Button,修正也很简单,指明我们要点击的按钮是导航栏上的 Workouts 就好
游客,如果您要查看本帖隐藏内容请 回复

UI recording
这个比较简单,新建一个空白的测试方法,光标移到方法内部起始位置,点击红色的 Record UI Test 小圆点会启动模拟器,此时你在模拟器上的操作会被 Xcode 记录下来转换成操作代码,也就是上一步写过的那些代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值