自动化测试代码可以“在你的睡着的时候”很好地帮你测试你的应用程序。它可以让你能够快速地跟踪你程序中的回归和性能方面的问题,这样你就不用担心你新增的功能会影响到你之前已经完成开发的程序了。
随着iOS4.0的发布,苹果公司同时发布了一个名为UIAutomation的测试框架,它可以用来在真实设备和iPhone模拟器上执行自动化测试。但官方关于UIAutomation的文档相当的有限,在网络上也没有太多的资源可以查找的。本文将向你展示你如何将UIAutomation整合到你的工作流程当中去。
作为基础知识的准备,你可以先看一下苹果公司关于UIAutomation的文档,另外还有一篇快速入门的介绍苹果Instruments的文档也值得看看,当然,如果你有一个免费的Apple开发者账号的话,你可以看一下WWDC2010-Session306–使用Instruments进行用户界面自动化测试的幻灯片或者视频。
除此之外,包括在Xcode中的OCUnit测试框架也可以用来为你的应用程序编写单元测试。
1.第一个UIAutomation测试脚本
2.处理UIAElement和元素可访问性(Accessibility)
4.高级交互
5.总结
1.你的第一个UIAutomation测试脚本
UIAutomation的功能测试代码是用Javascript编写的。UIAutomation和Accessibility有着直接的关系,你将用到通过标签和值的访问性来获得UI元素,同时完成相应的交互操作。
下面让我们来编写我们的第一段测试代码。
使用iOS模拟器
1.下载示例应用程序TestAutomation.xcodeproj,并打开它。这个项目是一个很简单的包含2个tab的tabbar应用程序。
2.确保选中如下图所示的“TestAutomation>iPhone5.0Simulator”模式(或许你已经切换成5.1了,因此它可能是iPhone5.1模拟器)。
3.启动Instruments(Product>Profile),或者通过I。
4.选择左边的iOSSimulator,然后再选择Automation模板,然后点击“Profile”。
5.Instruments就已经启动好后,然后直接开始录制了。这里先停止录制,(红包按钮或者R)。
6.在左边的Scripts窗口,点击“Add>Create”创建新的脚本。
7.在脚本编辑器里,输入下面的代码
8.重新运行这段脚本R(不需要保存)。脚本跑起来后,你可以在日志打完后停止它。
赞一个!我们就这样完成了我们的第一个UIAutomation测试用例。
使用iOS设备
你除了将你的测试用例运行模拟器上,也可以将它运行在一个真实的设备上。不过,自动化测试用例只能运行在支持多任务的:iPhone3GS,iPad,iOS>4.0等设备上。遗憾的是不管iPhone3G的系统版本是什么,都不支持。
下面是如何操作:
1.通过USB接口连接上你的iPhone。
2.选择“TestAutomation>iOSDevice”模式。
3.确保Developperprofile设置成Release模式(而不是Ad-HocDistributionprofile)。默认情况下,profiling是设置成Release模式的(因为没有必要将profile设置成Debug模式)。
4.启动测试(I)
5.后面的步骤请参考前面模拟器部分。
UIAElement层次结构
你可以通过InterfaceBuilder,或者通过在程序里设置isAccessibilityElement属性的方式来设置一个控件的Accessibility或者可被自动化。当你设置containerview(即:一个视图包含其它的UIKit元素)的accessibility时,你必须注意。你设置了整个View的accessibility将会“隐藏”它的子视图的accessibility,例如:在示例项目中,你不能将outlet视图设置成可访问的,否则它所有的子控件将都不可以访问了。在任何时候,logElementTree都是你忠实的朋友:它将当前界面的所有可被访问的元素都打印在日志里。
每一个可以被访问的UIKit控件都可以用一个Javascript对象来描述,它就是一个UIAElement。UIAElement有几个属性:name,value,elements,parent。你的主窗口包含很多的控件,它们是以UIKit层次的方式定义的,这些UIKit层次结构对应的是UIAElement的层次树。例如:前面的测试代码中,通过调用logElementTree,我们可以得到如下面所示的树结构:
+-UIATarget:name:iPhoneSimulatorrect:{{0,0},{320,480}}
|+-UIAApplication:name:TestAutomationrect:{{0,20},{320,460}}
||+-UIAWindow:rect:{{0,0},{320,480}}
|||+-UIAStaticText:name:FirstViewvalue:FirstViewrect:{{54,52},{212,43}}
|||+-UIATextField:name:UserTextvalue:TapSomeTextHere!rect:{{20,179},{280,31}}
|||+-UIAStaticText:name:Thetextis:value:Thetextis:rect:{{20,231},{112,21}}
|||+-UIAStaticText:value:rect:{{145,231},{155,21}}
|||+-UIATabBar:rect:{{0,431},{320,49}}
||||+-UIAImage:rect:{{0,431},{320,49}}
||||+-UIAButton:name:Firstvalue:1rect:{{2,432},{156,48}}
||||+-UIAButton:name:Secondrect:{{162,432},{156,48}}
你可以通过下面的代码来访问文本框:
vartextField=UIATarget.localTarget().frontMostApp().mainWindow().textFields()[0];你可以选择通过从0开始的索引或者这个元素的名称来访问这个元素,例如:你也可以通过下面的代码来访问文本控件。
vartextField=UIATarget.localTarget().frontMostApp().mainWindow().textFields()["UserText"];后一种方式更加清晰明了,应该多使用。你可以通过InterfaceBuilder设置UIAElement的name属性,
或者通过编写代码的方式:
myTextField.accessibilityEnabled=YES;myTextField.accessibilityLabel=@"UserText";你现在可以看到,通过accessibility属性可以被UIAutomation用来找到不同的控件。这非常的清晰,因为,第一,你只要学习一个测试框架;第二,通过编写自动化测试代码,你同时还可以保证你的程序是可以被访问的。因此,每一个UIAElement对象的子控件可以通过下面的方法进行访问:
buttons(),images(),scrollViews(),textFields(),webViews(),segmentedControls(),sliders(),staticTexts(),switches(),tabBar(),tableViews(),textViews(),toolbar(),toolbars()等等……
你可以通过如下代码在tabbar上访问第一个tab:
vartabBar=UIATarget.localTarget().frontMostApp().tabBar();vartabButton=tabBar.buttons()["First"];UIAElement结构层次非常的重要,你以后会常常用到它。而且你还要记住,你可以在随时通过调用UIAAplication的logElementTree来获得它的结构。
UIATarget.localTarget().frontMostApp().logElementTree();在模拟器上,你还可以激活Accessibility的检测器。启动模拟器,找到“Settings>General>Accessibility>AccessibilityInspector”,然后将它设为“打开”状态。
这个彩色的小框框就是Accessibility检测器了。当它收起来的时候,Accessibility就被关闭了,当它展开的时候,Accessibility就是打开的。你只要点击上面的箭头按钮就可以激活或者屏蔽Accessibility。现在,打开我们的示例程序,激活检测器。
然后,点击文本框,检查UIAElement的name和value属性(其实就是accessibilityLabel和accessibilityValue对应的NSObject类型的值)。这个检测器可以帮助你调试和编写你的测试代码。
模拟用户操作
让我们更进一步,模拟一些用户的交互操作。你可以简单地调用按钮的tap()来作一个点击操作:
vartabBar=UIATarget.localTarget().frontMostApp().tabBar();vartabButton=tabBar.buttons()["First"];//Tapthetabbar!tabButton.tap();你还可以调用UIAButtons的doubleTap(),twoFingerTap()。如果你不想操作具体的某个元素,你也可以直接根据屏幕上指定的坐标点进行操作,你可以这么用:
现在,让我们来练习一下:
下面是Test-1.js代码:
你还知道了如何访问键盘上的按钮,然后作点击操作:
本文由知平软件的DawsonLiu翻译,转载请注明出处。
知平软件致力于移动平台自动化测试技术的研究,我们希望通过向社区贡献知识和开源项目,来促进行业和自身的发展。
==============================
翻译:子玉冰,JX,为爱西行,杰西,bennaReview:蓝羽
所有的开发人员都需要测试他们的软件,并且,许多聪明的人为此创建了测试套件。测试套件是一组测试用例,也被称为单元测试,针对小“单元”代码,通常是一个特殊的方法或者类。
所以,如果你能设置一个系统能自动构建、测试,然后提交你的应用程序到测试人员,你会感兴趣吗?如果会,那么继续阅读吧!
GitHub-公共免费Git存储库,合作者管理,问题追踪,维基,下载,代码评审、图形,还有更多功能。
在本教程中,您将创建一个自动化的构建和测试系统GuildBrowser。GuildBrowser是一个简单的应用程序,允许您从受欢迎的游戏《魔兽世界》里浏览一个公会的成员们。您可以输入一个工会和一个域名看到UICollectionViewController上所有成员列表,正如你下图所示:
这个应用程序是由暴雪免费提供的魔兽世界API驱动的。您将使用AFNetworking框架对WOWAPI进行平稳的调用、获得JSON数据,这些数据包含工会和角色信息。
开始
在这篇教程中首先你将在GitHub上建立一个远程存储库。它将是你的远程“原始”存储库。它允许你在本地照常工作,并且当你准备与团队成员同步或发送一条新的构建给测试人员时,你将把所有你的本地修改提交到这个存储库。
尽管没有为原始地址输入密码,不过不要慌张。你会使用Git的SSH来进行访问,所以不需要密码。
你现在已经建立好了一个远程存储库!当然你也可以使用同样的步骤来为其他所有的Xcode工程进行设置。虽然远程存储库已经建立好,但是你仍然没有从本地Git存储库推送任何文件到远程Git存储库。我们接下来使用Xcode来完成这个任务。
回到你的工程,首先确定你已经提交了所有的本地修改。如果你有修改没有提交,那么Xcode将不允许你推送的远程存储库。下面是具体的步骤:
一旦推送完成,回到浏览器中,刷新界面,你提交的修改应该会显示在你的GitHub存储库中:
恭喜你,你已经学会如何将本地Git存储库连接到GitHub上的远程存储库,并将本地修改推送到远端。
持续集成
安装Jenkins
有几种不同的方法可以在Mac安装Jenkins:
1.你可以使用Jenkins网站上的安装器,它会作为守护进程启动并且运行Jenkins。这种方式很不错,因为它采用了MacOSX系统本身的启动系统,但是它会导致你需要做更多的步骤,才能让Xcode的编译正常运行。
2.你可以在终端里使用WAR文件(Java打包文件类型),它会用内建的servlet容器winstone来运行Jenkins。
3.你可以把WAR文件部署到你已经建立的应用容器内,比如Tomcat、JBoss等。
注:以上的命令是假设你的JenkinsWAR文件在你的下载目录里。如果你把WAR文件下载到了不同的目录,需要相应地更改命令中的路径。
注:你可能会发现为启动Jenkins创建一个别名是很有帮助的。在终端里,或是文本编辑器中显示隐藏文件,打开/Users/(username)/.bash_profile并且输入以下几行:
保存.bash_profile,打开一个新的终端,然后输入jenkins来启动Jenkins。
这会在8081端口上启动Jenkins服务。nohup是一个UNIX的命令,它可以让一个进程在你登出或是shell退出的时候,仍然保持运行状态。
以上的命令也会在/tmp/jenkins.log文件中记录日志。我会尽量去配置与已有的服务不冲突的端口。在浏览器中打开以下URL就能进入Jenkins:
配置Jenkins插件
这个就是你想要的:
当它完成安装后,你就可以看到以下界面:
现在在筛选框中输入ChuckNorris,然后找到ChuckNorris引用插件。
注:我的朋友叫我Charlie,但是我的真实名字是Charles。我父亲的中间名字叫做Norris。所以我曾经正式地宣称要把我的儿子命名为ChuckNorrisFulton…但是不幸的是我老婆没让我这样做:)
设置Jenkins的邮件通知
现在你可以收到来自Jenkins关于编译失败的通知。
创建一个Jenkins任务
注:ChuckNorris的程序没有版本号,因为他只写一次。如果用户反馈了一个bug或是一个需求,他们不会活着见到太阳升起的。
不要忘了点击保存按钮
如果一切正常,编译#1应该为蓝色,表示编译成功。
你的终端输出应该如下图所示,说明代码已从GitHub存储库导出到本地。
至此,你已经连接上GitHub,并且设置了邮件通知。下面紧接着我们来添加一些感兴趣的功能:测试,编译和上传项目工程到TestFlight。
Jenkins自动Xcode编译
在命令窗口输入下列代码:
你的代码窗口应该如图所示:
如果你错过了弹出的提示(例如,你正在追逐拿走你iPhone的两岁小孩^_^),你将会在终端输出中看到如下信息:
如果这发生了,重新运行项目,但是这次不要再错过提示(或者再不要让你两岁小孩拿走你的iPhone)。
当撰写这篇博文时,我真的不得不用findmyiPhone来定位我的iPhone。这个小家伙已经把手机塞进他玩具车的后备箱里!!
假设你没有错过提示,那么你会看到输出如下所示:
注:有一款叫做Xcodeplugin的用来构建iOS软件的Jenkins插件,它很不错,但我认为最好了解程序编译期间发生了什么。你可以用一些自己的编译脚本来达到与插件相同的效果。
更重要的是,依赖插件会带来一些风险。假设Apple改变运行机制,导致插件运行不正常,并且插件不再提供更新,如果你知道运行的来龙去脉,那么你依然能够适应。
单元测试
单元测试可以确保正在开发时代码输出保持不变。在建立了一个类将要做什么的期望后,我们编写单元测试以验证这些期望是否正确。单元测试也可以让你逐步看到输出,来逐步修改代码,这使得开发新功能和作大的修改更容易。
在项目中运用单元测试的另外一些好处是:
稳定性–一旦发现bug,可以加入新的测试,以确保相同的bug不再发生。
Xcode在SenTestingKit框架中内置了单元测试。当编写单元测试时,需注意以下两个方面:
应用程序VS逻辑测试target
接下来看一下Xcode中两种可用的类型不同的单元测试。
应用程序单元测试
应用程序单元测试基本上指需要使用UIKit控件或者在主应用程序环境中运行的任何代码。当创建一个包含单元测试的新项目时,这种类型的单元测试target是你得到的默认类型。
逻辑单元测试
从命令行运行测试,注意——没有触摸!
不幸的是,自Xcode4.5起,我们不被“官方”允许通过xcodebuild命令运行应用程序单元测试。这令人沮丧,因此你显然能在Xcode中运行应用程序的单元测试。因此,你将在下一节创建一个特定的逻辑测试target。
一些有创意的开发者已经针对Xcode.app内容中的测试脚本做了补丁。本教程中,你将坚持通过Jenkins运行你的逻辑测试并且通过Xcode运行应用程序测试。如果你对那些补丁很好奇,可以看看这些文件:
你将会得到一个错误信息,因为我们不能在模拟器中通过命令行工具运行应用程序单元测试(记住这在Xcode中能成功执行)。
一些聪明的骇客已经能运行应用程序测试,但是从Xcode4.4到Xcode4.5,脚本已经改变了,如果这么做的话结果需自己负责。
添加逻辑测试target
现在你要创建一个新的测试target用于你创建的脚本调用。
你应该看到构建成功了,但是测试失败了
根据错误信息的显示,单元测试还未被实现。让我们去修正它!
你的屏幕应该看起来像这样:
你现在准备好给你的测试target添加单元测试类。接着你就能通过jenkins任务调用这个target。在这个盛宴的下一部分你会做到这些。保持学习欲望……和不满足!
何去何从?
在这几天,我们将发布这个教程的第二部分。同时,如果你有任何问题和意见,请加入如下的论坛讨论!