客户端 UI 自动化测试是大多数测试团队的研究重点,本文介绍猫眼测试团队在猫眼 iOS 客户端实践的基于 KIF 的 UI 自动化测试和持续集成过程。

一、测试框架的选择

iOS UI 自动化测试框架有不少,其中 UI Automation 是 Apple 早期提供的 UI 自动化测试解决方法,用 JavaScript 编写测试脚本,通过标签和值的可访问性获得 UI 元素,来完成相应的交互操作。

一些第三方 UI 解决方案以 UI Automation 为基础,对其进行补充和优化,包括扩展型 UI Automation 和驱动型 UI Automation。

  • 扩展型 UI Automation 采用 JavaScript 扩展库方法提高 UI Automation 的易用性,常见的框架有 TuneupJs、ynm3k。
  • 驱动型 UI Automation 在自动化测试底层使用了 UI Automation 库,通过 TCP 等通信方式驱动 UI Automation 来完成自动化测试。这种方式下,编辑脚本的语言不再局限于 JavaScript 。常见的框架有 iOSDriver、Appium。

还有一些其他的第三方解决方案,常见的框架类型有私有 API 型和注入编译型。

  • 私有 API 型框架直接使用 Apple 私有 API 对 UI 界面进行操作。常见的框架主要有 KIF。
  • 注入编译型框架在编译时注入一个 Server 到 App 内部,通过 Server 对外通信完成 UI 操作指令的执行。常见的框架有 Frank、Calabash。

Xcode 7发布后,Apple 提供了一种新的 UI 自动化测试解决方法——UI Testing,它基于 XCTest 测试框架,通过控件的可访问性来定位和获取控件,并提供了多种 UI 操作 API,使用源码语言,能方便地进行调试。 我们在以上分类中挑选具有代表性的自动化框架:UI Automation、Appium、KIF、Frank、UI Testing 进行对比,下表是这几种测试框架的特点对比:

考虑选择测试框架的几种影响因素。首先,使用的语言和框架决定了测试人员的持续性学习成本,iOS 测试人员对 Objective—C 和 XCTest 熟悉和掌握程度高,不需要消耗额外的学习成本,人员更替时的接手成本也相对较低;其次,测试框架支持的 UI 操作的丰富性决定了测试用例的覆盖完整度,使用私有 API 的测试框架支持的 UI 操作较为全面,而同时支持 UIWebView 的测试框架则更占优势;另外,App 程序 UI 变化快,使用开发效率高、调试方便的测试框架能使我们在适应新 UI 变化、新需求时获得更小的投入产出比。 综合以上考虑,KIF 框架已经展现了他的优势,并且 KIF 使用 XCTest 框架,使得其测试流程 iOS 程序的单测无异,可完全复用单测的持续集成流程,维护持续集成的成本相对降低;另外,KIF 是一个活跃的开源测试框架,可扩展性好,升级更新快,有活跃社区来探讨和解决使用过程中遇到的问题。鉴于上述优势,我们选择了 KIF 作为 iOS 的 UI 自动化测试框架。

二、KIF 自动化实施

KIF 利用 Apple 给所有控件提供的辅助属性 accessibility attributes 来定位和获取元素,完成界面的交互操作;结合使用 Xcode 的 XCTest 测试框架,拥有 XCTest 测试框架的特性,使得测试用例能以 command line build 工具运行并获取测试报告。

下面介绍如何进行 KIF 自动化实施。

1. KIF 搭建

KIF 以第三方库的形式编译运行于工程中,搭建 KIF 之前,应该确保工程在 Xcode 上编译运行通过。 KIF 基于 XCTest 框架,继承了 XCTest 的所有特性。和 XCTest 一样,我们首先应该在工程项目中创建基于 Cocoa Touch Testing Bundle 模板的 Target ,并确保创建的 Target 的属性有如下设置:

  • “Build Phases”:设置 Target Dependencies , UI 自动化测试固然要依赖应用程序的 App 产物,所以需保证应用程序 Target 被添加在 Test Target 的 Target Dependencies 中。
  • “Build Settings”:     设置 “Bundle loader” 为:$(BUILT_PRODUCTS_DIR)/MyApp.app/MyApp;     设置 “Test Host” 为:$(BUILT_PRODUCTS_DIR);     设置 “Wrapper Extensions” 为:xctest。

项目的设置准备好后,需要安装 KIF 库源码到项目。即可开始 KIF 编写用例之旅。 KIF 通过属性值(AccessibilityLabel, AccessibilityIdentifier, AccessibilityTraits,Value…)在界面中定位元素。为了获取到目标元素,我们必须先设置元素的 accessibility 属性。如下,想要获取程序中一个列表的 cell 元素,我们给列表的 cell 控件设置 accessibility 属性(如左图所示),设置为“Section XX Row XX”,编译运行,即可获得历史列表的 cell 元素;用模拟器的 Accessibility Inspector 抓取到了这个历史列表元素(如右图所示):

KIF 为我们提供了对有 accessibility 属性控件的操作接口,如下最简单的两个操作接口:

  • 点击一个元素:- (void)tapViewWithAccessibilityLabel:(NSString *)label;
  • 等待一个元素的出现:- (UIView *)waitForViewWithAccessibilityLabel:(NSString *)label。

在新建的 Target 同名目录下增加一个继承自 KIFTestCase 的类,类中编写我们的用例,完成对界面的点击和验证,如下:

以上步骤都完成后, 基于KIF的简单用例便搭建完成,点击 Product->Test 或者快捷键 (⌘U) 即可看到我们的用例自动运行起来了。

2. 用例编写与组织

(1)accessibility 属性设置

accessibility 属性是 Apple 给视觉障碍人群提供完全无障碍使用的基本属性,该属性表明了 UI 元素的可访问性、是什么、做什么以及会触发什么样的操作。原生的 UIKit 控件默认提供了这些信息,然而,自定义的控件则需要对该属性进行设置,设置方式可参考下面几点:

  • 设置方式:找到页面元素所属的代码文件,再到代码中找到该类的实现,在相应代码处添加其属性。
  • 查看方式:设置好后,开启模拟器的 Accessibility Inspector 功能,即可看到控件的 accessibility 属性。
  • 设置建议:设置的 AccessibilityLabel 属性值要有实际意义(用户可理解),因为设置这个属性后用户可以通过 VoiceOver 访问;用户不可访问的控件,比如某些放置控件的容器等应该设置为 AccessibilityIdentifier 。

(2)用例常用操作接口:

  • UI交互操作( KIFUITestActor.h 中可查阅):
 tapThisView:                - (void)tapViewWithAccessibilityLabel:(NSString *)label;waitForView:                - (UIView *)waitForViewWithAccessibilityLabel:(NSString *)label;注意:函数返回了对应View的指针,可以对返回值取数据,从而进行一些判断enterTextIntoView:       - (void)enterText:(NSString *)text intoViewWithAccessibilityLabel:(NSString *)label;tapRowOnTableView:          - (void)tapRowAtIndexPath:(NSIndexPath *)indexPath inTableViewWithAccessibilityIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(5_0);dismisses a system alert:   - (void)acknowledgeSystemAlert;

扩展:我们还可以对 KIFUITestActor 类进行扩展,利用 KIFUITestActor 中的私有函数,使 AccessibilityIdentifier 代替 Label 识别元素,完成 tapThisView 、waitForView 等操作。

  • 用例集操作( KIFTestCase.h 中可查阅):
   - (void)beforeAll; 在本类中第一个 test case执行前执行一次用处:执行本类中各个测试函数的公共操作注意:因为不能保证这个方法与 test case 是同一个类实例,所以不能用来设置实例变量的值,但是可以设置静态变量- (void)beforeEach; 在每一个 test case 执行前执行一次用处:执行各个函数需要的测试环境注意:因为确保这个方法与 test case 是同一个类实例,所以可以用来设置实例变量- (void)afterEach; 在每一个 test case执行后执行一次用处:用来将 App 恢复至 test case 之前的状态,可以包含一些条件判断逻辑,从失败的 test case 中恢复,以确保不影响之后的测试- (void)afterAll; 执行完测试类的最后一个 test case 后执行一次用处:用于将 App 恢复至测试的初始状态
  • 系统的功能实现( KIFSystemTestActor.h 中可查阅):
   模拟用户旋转设备:         - (void)simulateDeviceRotationToOrientation:(UIDeviceOrientation)orientation;对当前屏幕截图并存储到硬盘中:- (void)captureScreenshotWithDescription:(NSString *)description;

(3)用例组织

设计实现单个测试用例步骤如下: * a. 设置测试所需要的环境; * b. 测试用例的测试逻辑; * c. 恢复App至此次测试前状态。

a、c步骤可用 beforeEach、afterEach 来实现,这样保证了每个用例之间的独立性和用例运行的稳定性。 一般来说,可将用例按功能分成若干个用例集,每个用例集按校验点或者功能点分成若干个用例,这样方便测试用例的管理和维护。 某些含有耗费时间多、耗费资源多的公共操作的用例可以集合成一个用例集,在用例集运行前统一执行。设计实现用例集步骤如下:

  • a. 设置用例集需要的环境、公共操作;
  • b. 设计各个用例;
  • c. 恢复 App 至用例集测试的初始状态。

a、c步骤可用 beforeAll、afterAll 来实现,下图展示了一个用例集的书写示例:

#import "TimerTests.h"
#import "KIFUITestActor+AccessibilityLabelAddition.h"
#import "KIFUITestActor+IdentifierAdditions.h"
#import "KIFUITestActor+TimerAdditions.h"
@implementation TimerTests
- (void)beforeAll
{[tester setDebugModel];
}
- (void)afterAll
{[tester resetDebugModel];[tester clearHistory];
}
- (void)beforeEach
{[tester setDebugModel];
}
- (void)afterEach
{[tester clearParams];
}
- (void)testNameedTask
{[tester enterText:@"myTask" intoViewWithAccessibilityLabel:@"Task Name Input"];[tester enterWorktime:10 Breaktime:4 Repetitions:5];[tester tapViewWithAccessibilityLabel:@"Start Working"];[tester waitForViewWithAccessibilityLabel:@"myTask"];[tester waitForViewWithAccessibilityLabel:@"Start Working"];
}
- (void)testnoNameTask
{[tester enterWorktime:10 Breaktime:4 Repetitions:5];[tester tapViewWithAccessibilityLabel:@"Start Working"];[tester waitForViewWithAccessibilityLabel:@"myTask"];[tester waitForViewWithAccessibilityLabel:@"Start Working"];
}
- (void)testPresetTask
{[tester tapViewWithAccessibilityLabel:@"Presets"];[tester tapRowAtIndexPath:@"Classic" inTableViewWithAccessibilityIdentifier:@"Presets List"];[tester tapViewWithAccessibilityLabel:@"Start Working"];[tester waitForViewWithAccessibilityLabel:@"myTask"];[tester waitForViewWithAccessibilityLabel:@"Start Working"];
}
@end

上述代码中,我们看到许多封装函数。为保证用例结构清晰明朗,我们借鉴 selenium pageObject 的设计方式, 遵循如下规则:

  • a. 将页面上的对元素的发现、操作处理抽象为相应的类,返回操作结果;
  • b. 封装尽可能多的工具类;
  • c. 测试用例只关注用例逻辑,步骤尽量简洁。

如下图所示,在用例集 test suite 中,我们只保持清晰的用例逻辑;非用例逻辑的动作封装成相应地用例集的类 test suite additions ;因为 KIF 的开源性,我们还可以利用 KIF 的私有 API 封装我们需要的工具 Tools 类。

(4)用例的运行独立和 retry 机制

失败用例是不可避免的,上述用例的组织方式,降低了用例间的依赖性,但是并不能完全消除失败用例对后续用例执行的影响。如果能让每个用例独立启动 App 执行 case,则能保证后执行用例不受先执行失败用例的影响。如果在 case 运行失败后,还可以进行 retry 重试,则能提高用例运行的稳定性。xctool 工具能给我们带来这样的功能,我们用 xctool 命令先 build-tests 构建 app,然后循环启动 app 来 run-tests 用例,用例失败后,重新执行。下面是一个 xctool 独立运行用例的简单示例:

xctool build-tests -workspace myApp.xcworkspace -scheme myKIFTestScheme -sdk iphonesimulator -configuration Debug -destination platform='iOS Simulator',OS=8.3,name='iPhone 6 Plus'array=( TimerTests HistoryTests )for data in ${array[@]}
doxctool  -reporter pretty -reporter junit:tmp/test-report-tmp.xml -workspace myApp.xcworkspace -scheme myKIFTestScheme run-tests -only myKIFTestTarget:${data}  -sdk iphonesimulator -configuration Debug -destination platform='iOS Simulator',OS=8.3,name='iPhone 6 Plus'
done

三、KIF 自动化的持续集成

1. 持续集成的意义与 UI 自动化测试的用例选择

持续集成是一个自动化的周期性的集成测试过程,从检出代码、编译构建、运行测试、结果记录、测试统计等都是自动完成的,无需人工干预。我们的项目都是团队协作开发,采用持续集成的优势显而易见:

  • 尽早尽快地发现集成错误,保证团队开发人员提交代码的质量,减轻软件发布时的压力;
  • 自动完成集成中的环节,有利于减少集成过程的重复工作以节省时间、费用和工作量;

持续集成最大的好处在于能够尽早高效发现问题,降低解决问题的成本。而发现问题的手段主要就是测试。 根据 Martin Fowler 的测试理论,测试应该遵循如下测试金字塔组合,测试金字塔最底层是单元测试,然后是集成测试,继而是面向应用程序服务层的中间层测试,最高层是面向用户的业务逻辑测试:

![](https://awps-assets.meituan.net/mit-x/blog-images-bundle-2016/41eeb72b.png)

测试自动化的测试层级越多,持续集成平台就能产生越大的价值。 UI 测试目标是覆盖最核心的代码,尽可能去掉依赖,让不稳定因子降到最低,这样既保证自动化测试层级的全面性,又保证持续集成的稳定构建,降低测试的投入产出比。因此,在我们的 UI 自动化测试中,我们选择核心功能的冒烟用例来完成持续集成中的测试金字塔。

2. Jenkins 上完成基于 KIF 的 UI 自动化持续集成搭建

Jenkins 是一个开源的持续集成工具,提供了一种易于使用的持续集成系统,使开发者从繁杂的集成中解脱出来,专注于更为重要的业务逻辑实现上。 Jenkins 以 Job 为单位运行项目,一个 Job 的工作流程为:在指定的时机,选择合适的 salve 节点,从版本管理系统上获取对应的源码,使用命令行脚本或者 maven 或者 ant 进行构建,构建后归档文件,处理报告,如果构建失败那么就通过邮件进行反馈等。 Job 的触发时机主要有3种选择:

  • “Build after other project are build”:表示在其他某个项目build后触发,比如我们可以在某个提测Job构建之后,立即构建我们的 UI 自动化来验证这个提测的可行性;
  • “Build periodically”:表示按时间触发,我们可以选择这个让 Job 做 Daily Build 来进行持续构建观察;
  • “Poll SCM”:表示允许用户让 Jenkins 定期查询某一个项目的代码库,如果有代码变动则触发执行任务,这种触发非常适合集成测试项目,以此验证代码库变动是否能测试通过。

我们希望在代码改动发生的时候就做到尽早发现代码改动带来的问题,所以使用 “Poll SCM” 在当代码仓库有新的 pull request 的时候触发相应 Job 完成构建,Job 的执行结果作为这个 pull request 能否合入的衡量指标之一;同时为支持客户端支持 daily build ,Job 使用 “Build periodically” 在每天 daily build 打包前完成一次自动构建。 Job 需要支持命令行构建才能实现持续集成,如上一部分提到,我们可以借助 xcodebuild/xctool 实现单命令行构建。同时为了衡量 Job 的执行结果,我们需要在 Job 执行完成后生成相应的测试报告和代码覆盖率报告,使用 xcodebuild/xctool 这样的命令行工具,只需要配置相关的参数即可获取相应的 XML 测试报告文件。 Jenkins 中 JUnit Plugin 插件可以将 XML 形式的测试报告转化成一种随时间推移的测试结果图表,向我们展示测试的结果和测试的稳定性; Cobertura plugin 插件可以将 XML 形式的覆盖率文件转化成一种随时间推移的代码覆盖率图表。如下图是 Job 中测试报告的代码覆盖率和测试结果的示例,通过下面的图表,我们可以清晰地看到测试是否通过,检查代码的测试覆盖范围,并对比历史的测试结果和代码覆盖率来推断和定位问题。

3. KIF 自动化测试在 Jenkins 持续集成过程中遇到的问题

(1)设备重置

我们的测试用例覆盖了第一次安装启动的操作。在初期,这个用例经常失败。经过排查发现,持续集成系统中的模拟器设备重置操作并没有覆盖所有的设备,UI 测试 Job 运行时,Job 选择的模拟器设备上可能遗留了其他 Job 构建的相同的 app 产物,导致我们的 Job 构建产物并不是第一次安装启动。所以在脚本中我们遍历所有模拟器设备,将其进行重置。

(2)键盘敲击延迟

我们的测试用例在输入框输入文字时,经常出现输入不全而导致失败的问题。比如在输入框中输入 ‘beijing’ ,失败后提示:Failed to get text in field; instead, it was ‘beiji’ 。经过排查,发现持续集成系统中的机器性能有高有低,在低性能机器中更容易发生此问题,再研究 KIF 框架源码发现,KIF 默认设置的键盘敲击时延为一个常数,对于低性能机器来说这个敲击时延较短,容易漏掉输入,所以我们在 KIFTypist.m 源码文件中适当增加 (NSTimeInterval) keystrokeDelay 的时长来避免输入不全的问题。

(3)多个系统弹窗确认

前面我们提到过,KIF 支持对系统弹窗的处理,即接口 acknowledgeSystemAlert ,它能帮我们确认一个系统弹窗。但是我们的应用程序在启动时系统弹窗并不止一个,并且在不同设备上,因系统设置不同,系统弹窗的个数是不确定的。所以,直接使用 acknowledgeSystemAlert 并不能帮我们解决问题。因为 KIF 的开源性,我们在 KIF 框架源码 acknowledgeSystemAlert 函数中做了一次 while 循环处理,处理了出现的任意多个系统弹窗的情况,从而解决了问题。

参考文献

  1. Automate UI Testing in iOS:https://developer.apple.com/library/tvos/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/UIAutomation.html
  2. Appium 官网介绍:http://appium.io/slate/cn/v1.2.0/?ruby#appium
  3. Frank 官网介绍:http://www.testingwithfrank.com/
  4. KIF 源码库:https://github.com/kif-framework/KIF
  5. iOS UI Testing with KIF:http://www.raywenderlich.com/61419/ios-ui-testing-with-kif
  6. The current state of iOS automated functional testing:http://watirmelon.com/2013/11/04/the-current-state-of-ios-automated-functional-testing/
  7. Page Object:http://martinfowler.com/bliki/PageObject.html
  8. Test Pyramid:http://martinfowler.com/bliki/TestPyramid.html
  9. Continuous Integration:http://www.martinfowler.com/articles/continuousIntegration.html
  10. xcodebuild:https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/xcodebuild.1.html
  11. xctool:https://github.com/facebook/xctool
  12. Jenkins 官网介绍:https://wiki.jenkins-ci.org/display/JENKINS/Home
  13. JUnit Plugin:https://wiki.jenkins-ci.org/display/JENKINS/JUnit+Plugin
  14. Cobertura plugin:https://wiki.jenkins-ci.org/display/JENKINS/Cobertura+Plugin
  15. Xcode 7 UI Testing:https://developer.apple.com/videos/play/wwdc2015/406/

基于 KIF 的 iOS UI 自动化测试和持续集成相关推荐

  1. ios ui自动化测试_Xcuitest的ios自动化ui测试

    ios ui自动化测试 Who knew automated UI Testing could be so easy! Well, I guess Apple did. Automated UI Te ...

  2. Jenkins——Jenkins介绍+基于云平台的Jenkins安装和持续集成环境配置(插件+用户权限+凭据+Maven打包)

    Jenkins--Jenkins介绍+基于云平台的Jenkins安装和持续集成环境配置(插件+用户权限+凭据+Maven打包) 持续集成及Jenkins介绍 软件开发生命周期 什么是持续集成? 持续集 ...

  3. 基于Jenkins的开发测试全流程持续集成实践

    今年上半年一直在公司实践CI,本文将上半年来的一些实践总结一下,可能不太完善或优美,但的确初步解决了我目前所在项目组的一些痛点.当然这仅是一家之言也不够完整,后续下半年还会深入实践和引入Kuberne ...

  4. 持续集成与持续部署(四)01-Jenkins——简介-基于Java开发的一种开源持续集成工具

    持续集成与持续部署(四)01-Jenkins--简介-基于Java开发的一种开源持续集成工具 Jenkins 使用简介 Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控 ...

  5. 软件自动化测试培训内容,软件自动化测试与持续集成实践培训方案

    一.背景概述 为响应工业和信息化部信息化高端人才培养的号召,中国信息化培训中心特推出了软件自动化测试与持续集成实践培训班,希望通过专业的培训与业界真实案例来全面提高学员软件自动化测试水平,从而更好地服 ...

  6. 干货 | 基于 BDD 理念的 UI 自动化测试在携程度假的应用

    作者简介 Leo Li,携程高级软件工程师,负责度假 BDD-Test UI 自动化测试框架的研发.维护和迭代等工作. 如今无论大公司还是小公司都越来越重视测试质量.并且前端领域越来越繁荣,前端工程也 ...

  7. 基于图片识别的 UI 自动化测试工具 SikuliX

    简介 SikuliX 是一款开源的 GUI自动化测试工具,它的前身是 Sikuli(上帝之眼).SikuliX 在设计上使用到了图片识别技术,会通过不停的对桌面进行截图,然后与预期图片进行比较来判断交 ...

  8. createprocess失败代码2_DevOPS | 基于sonarqube、jenkins和gitlab的持续集成代码检查

    在持续集成过程中,针对代码的静态检查一般有两个时间点:一是提交代码的时候.二是提交merge request的时候,如果代码检查不通过,则可以直接拒绝此次代码提交或者合并.下面我们介绍下在常见的dev ...

  9. android studio云测,Android studio 下的robotium自动化测试和持续集成

    [背景介绍] Android Studio 是一个Android开发环境,基于IntelliJ IDEA. 类似 Eclipse ADT,Android Studio 提供了集成的 Android 开 ...

最新文章

  1. Windows7/10上配置OpenCV3.3.0-Python3.6.2操作步骤
  2. 4、jQuery实现的全选、反选和不选功能
  3. Java各种数据类型详细介绍及其区别
  4. BRCM5.02编译一 : 缺少工具链路
  5. 仿京东左侧二级导航条
  6. UImenuController
  7. git初使用(本地创建后第一次提交到git)
  8. 高级程序设计c语言试卷答案,高级程序设计语言半期试卷答案.doc
  9. 推荐系统中使用ctr排序的f(x)的设计-传统模型篇
  10. golang实现聊天室(三)
  11. Scott Mitchell 的ASP.NET 2.0数据教程之五:: 声明参数
  12. (SQL)使用Excel连接数据库
  13. 微信分享代码申请key教程
  14. 侍魂微信新服务器2019,侍魂胧月传说手游2019年5月14日微信问答试炼答案
  15. macOS wifi 密码 查看
  16. Vue.js---关闭语法检查
  17. 深度整形外科:使用手绘草图进行鲁棒且可控的图像编辑
  18. 区块链技术再出新玩法——解决跨境支付问题
  19. percona-tool文档说明(总)
  20. 计算机多媒体教学的缺点,多媒体教学与传统教学的优劣势探讨-20210716004535.docx-原创力文档...

热门文章

  1. 用MS SQL Server事件探查器来跟踪数据库的操作
  2. skiplist原理与实现
  3. Postgre体系结构图
  4. C语言实现树,你一定看得懂
  5. mkimage command not found
  6. 自己做计算机三级,计算机三级辅导:自己“做”软驱
  7. php 公众号验证回调方法_微信公众号关键词自动回复设置方法!
  8. python中split函数_python strip()函数和Split函数的用法总结
  9. Express接口案例 使用jsonwebtoken
  10. Chapter6_Vocoder