您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
 订阅
  捐助
iOS应用UI自动化测试图文攻略
 
作者:close_marty来源:CSDN 发布于 2015-03-16
   次浏览      
 

iOS的自动化测试框架可分为两种:注入式和非注入式。注入式的框架通常会提供一些Lib或者是Framework,要求测试人员在待测应用的代码工程中导入这些内容,框架可以通过他们完成对app的驱动,典型的比如monkeytalk。非注入式的框架则是通过苹果提供的instruments工具,调用官方的接口函数,实现对app的相关操作,典型的就是uiautomation,appium

区别:

注入式:可获取app内的数据;可操作空间大,不受官方限制;可在windows平台进行测试;但是需要在待测项目中增加第三方的部分,使得测试的内容和实际发布的内容并不一致

非注入式:待测内容和最终的上线内容保持一致;测试无需源码;但是受官方限制,一些功能无法实现,而且环境要求必须使用os x平台的Xcode。

采用instruments的原因:

1. 测试工具和测试文档均由苹果官方维护

2. 无需额外前置工作,可以直接对提测的app进行测试

3. 支持录制

1. 环境

mac:

OS X

Xcode-instruments

其中,Xcode通过mac上的App store即可直接安装使用,instruments为Xcode自带的工具集,无需单独下载

iPhone/iPad:

待测app

其中,待测app如果是运行在真机上,则一定需要用开发者证书签名,采用发布证书或者是企业证书打包的应用无法用以真机测试。

2. 准备工作 for 真机

1)手动安装应用到iPhone上,并连接iPhone到mac。

PS:如果该设备是第一次连接这台mac,需要等待organizer完成识别和同步工作才能使用。

PPS:如果organizer没有自动启动,可以通过Xcode->window->organizer,手动启动该程序。

2)启动instruments工具集,并选择Automation,进入测试工具的主界面

启动方式:打开Xcode,选择Xcode->Open DeveloperTool->Instruments

PS:启动instruments之后,可以在下方Doce菜单中,选中instruments的图标,右键选择在Dock中保留,便于下次快捷启动该工具,不用每次都去启动Xcode。

3. 准备工作 for 模拟器

方式一:从源码直接启动instruments,适用于有源代码工程的情况。

1) 双击*.xcodeproj 文件打开工程

2) 在Xcode中,选则Produce->Profile,该命令会首先build整个工程,在成功之后,自动启动instruments工具集,然后手动选择Automation工具即可。

方式二:使用已经编译完成的并且可用于模拟器运行的*.app文件,有无源码均可

1) 手动启动instruments工具集并进入Automation,选择待测的*.app文件即可

PS:运行于模拟器和真机的程序需要采用不同的方式进行编译,不能通用的。平时的提测包都是用于真机运行的,无法在模拟器运行。

2) 如果有源码,可以在选择模拟器和对应的iOS版本之后,直接Run到模拟器中,然后在需要测试时,通过Automation选择到模拟器目录下的该app文件也可以。

模拟器中的应用可以在这个路径下面找到:

/Users/用户名/Library/Application Support/iPhone Simulator/iOS版本/Applications/

该路径下载就是模拟器中的已安装的所有应用程序,找到待测试的*.app即可。

目前我测试的是一块视频应用,播放器底层的一些库运行在模拟器上会有问题,所以我的后面的所有内容都以运行于真机的环境为准。

4. 从录制开始

1) 选择待测试的应用。

在左上方的choose target上点击,选择当前连接的设备,以及该设备上已安装的待测试app

2) 聚焦到script区域,点击该区域下方的红色按钮

如果上一步选择的设备和app正确的话,就可以看到iPhone或者iPad上的应用被启动起来了(如果之前就已经启动了,Automation工具会先结束掉设备上的进程并重新启动)

应用启动完成之后,就正式开始录制模式了,此时在真机上对应用进行的任何操作,都会被录制成脚本的形式显示在script区域。由于用户操作和脚本录制同时在进行,所以此时的操作通常都会呈现出响应慢或者卡顿频繁的情况。

下面这段是登录帐号a然后注销帐号的一系列操作的录制结果

点击停止按钮,终止录制过程,然后点击回放按钮,开始回放已经录制好的脚本,我们可以看到应用重启并执行之前的一系列操作。

到目前为止,对操作的录制就算基本完成了,在script区域右键export即可以到处录制的js脚本。需要的时候,在脚本管理区域scripts通过点击add->Import的方式,导入js脚本并运行即可。

但是上面的脚本存在很多问题。

1) 脚本中有一行绿色的提示信息://Alert detected……

2) 实际执行会发现最后一行代码会出错。

3) 如果wifi的网络状况不是很稳定,这段脚本基本都会以失败告终。

4) 每行代码都很长,并且不利于阅读

5) 没有检查点的测试脚本都是在耍流氓

这些问题,我们在接下来一一解决。

5. 自己动手,丰衣足食

录制的代码可维护性和健壮性都很差,并且缺少必要的检查点,所以实际效果非常差。这时就需要自己编写测试代码,采用按需定制的方式来实现自动化。

首先一定要记住这个地址:

https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/_index.html

这是苹果官方的参考文档,列举出了UI Automation JavaScript library的所有类,对app的各个操作最终都是通过这些api来实现的。

自动化的常见步骤通常是三步:定位,操作,检查

1) 定位元素

UIAutomation通过层级访问的方式,定位到某一个具体元素。苹果提供了一个logElementTree()的方法,打印控件树。在新建的测试脚本中,加入下面这行代码:

Target.logElementTree();

执行这段代码(点击左上角的红色按钮),我们可以看到输入了下面的东西

【截图】

上图就是整个app的UI层级结构,当你需要定位到某个元素的时候,可以按照这个层级一层一层的往下访问,直到目标元素。比如需要定位到播放记录。

var target= UIATarget.localTarget();

var app =target.frontMostApp();

var window= app.mainWindow();

//进入登陆界面

varloginButton = window.scrollViews()[0].buttons()["请点击登录"];

其中:

UIATarget 对象代表待测应用所在环境的最高层级UI,在这里localTarget()表示运行app的这台iPhone设备

UIAApplication对象代表app层级的UI,这里通过frontMostApp()方法得到的对象,就是指正在运行的影音iPhone app。

UIAWindow对象代表app中window层级的UI,这里通过mainWindow()方法得到的对象,指当前app中的主窗体,一个app的当前界面通常只会有一个主窗体。

实际项目中,不同元素的差异都是从window层级开始的,在window层级往上,都是一样的。

2) 操作元素

上面一部完成就已经可以做到元素的定位,现在需要对这个元素进行操作,最常见的就是tap

loginButton.tap();

对于不同的元素,提供的具体操作方法会存在差异,详情需参考官方文档。进入登录界面后,需要出入帐号密码,也是同样的步骤:

//定位输入框

varnameField = window.textFields()[0];

varpwField = window.secureTextFields()[0];

//操作

nameField.setValue("1@1.pp");

pwField.setValue("ppp111");

//点击执行登陆

window.buttons()["loginlogin button"].tap();

3) 检查结果

操作完成后,需要检查结果是否符合预期。

if(window.scrollViews()[0].staticTexts()[0].name() == "请叫我雷锋"){

      UIALogger.logPass("测试通过");

}else{

      UIALogger.logMessage(window.scrollViews()[0].staticTexts()[0].name());

      UIALogger.logFail("测试失败");

}

上述的这几个步骤,就算完成了UIAutomation的hello world,但是前面抛出的问题并没有完全解决。

6. 逐个击破

1)延迟

上面这段代码,在实际运行时,基本是会一直测试失败的,原因是从开始登录,到展示用户信息,需要一定的时间,点击登录按钮操作之后,立刻去判断用户信息也就一定会失败的。另外,网络波动,或者是app/测试机可能出现的卡顿,是造成测试脚本的频繁失败的原因之一,因此我们需要某些操作之后,人为增加一些必要的延迟,等待操作的完成,从而增强代码的稳定性。苹果在target层提供了一个方法delay(Number timeInterval),该方法用于延迟脚本的执行,我们可以在必要的地方增加该方法。

target.delay(3);

2)弹框

弹框alert在iOS中比较特殊的类型,它不属于app层级的,也不能像操作普通元素一样去操作alert,苹果提供了一个专门的处理方法,UIATarget.onAlert,当弹框出现时,测试引擎会自动调用这个方法来处理弹框。

【待完善】

3)Log

UIALogger主要就用于输出各种类型的日志。包括logStart,logPass,logFail,logMessage,logDebug,logWarning ,logError

前三个通常用来区分一个测试用例,logstart表示一个测试的开始,直到logPass或者是logFail为止。后四个用以在测试过程中输入不同级别的日志。

7. tuneupjs

tuneupjs是一个用以优化uiautomation的第三方js库,网站地址: http://www.tuneupjs.org/

1)测试用例

Tuneupjs提供了test方法,用以表示一个测试用例,该方法有两个参数:测试用例的名称和一个测试内容的function,在这个方法中,你无须使用logStart,logPass,logFail这一类的方法,Tuneupjs已经在test方法的实现中,做了相应的处理,一个test方法就对应一条测试用例,当这个test方法中的function正常的执行完毕,这个case就会变为pass,如果执行过程中有任何异常抛出,test就会停止并把这个case置为fail。

test("登录测试",function(target,app){

      app.tabBar().buttons()[2].tap();

      var loginButton =window.scrollViews()[0].buttons()["请点击登录"];

       //如果已登录则先注销账号

      if (!loginButton.isVisible()) {

            window.scrollViews()[0].buttons()[1].tap();

            window.buttons()["退出登录"].tap();

            target.delay(1);

      }

      //如果未登录则执行登录

      if (loginButton.isVisible()) {

            log("登录帐号*****");

            loginButton.tap();

            var nameField =window.textFields()[0];

            var pwField =window.secureTextFields()[0];

            nameField.setValue("*****"); //帐号

            pwField.setValue("*****");  //密码

            window.buttons()["login loginbutton"].tap();

            target.delay(3);

      }   

      assertEquals(window.scrollViews()[0].staticTexts()[0].name(),"请叫我雷锋","登录失败");

});

采用test方法来组织测试用例有一个很明显的好处,该方法已经做好了异常的处理,出现的任何异常都会被当前所在的test方法捕获到,而不会把异常抛给最上层,避免了一旦出现异常就会导致整个测试停止执行的情况。一个test失败之后,不会影响下一个test的执行。

2)断言

这是Tuneupjs的又一个优势,它提供了一系列的断言方法,可以用以对测试结果进行判断,并且在断言失败时自动截图保存,基础的断言方法包括assertEquals assertNotEquals assertTrue assertFalse assertNull assertNotNull等,在一个test中,一旦出现断言失败,这条用例就会变为fail状态。

3)Retry方法

这是一个重试方法,配合断言使用效果更佳。retry()的必要参数就是一个function,如果function中出现了断言失败,或者是有异常抛出,该方法会再次尝试执行这个function,直到function的内容全部通过或者是最终超时。默认的重试次数是3次,每次重试间隔0.5秒,可以为retry()方法设置第二和第三个参数来修改这两个值。retry()方法比单纯的使用官方的delay()方法要更加合理。

retry(function(){

      assertEquals(window.scrollViews()[0].staticTexts()[0].name(),"请叫我雷锋","登录失败");

},5,1);

4)Test方法中手动抛出异常

有时候出于需要,我们需要在某个测试用例中手动抛出异常,让用例失败,可以使用tuneup提供的fail(message)方法,也可以自己直接抛异常throw(exception),test方法捕获异常之后会认为当前的用例失败,跳出并执行下一条用例。

5)命令行启动UIAutomation

UIAutomation本身是支持命令行启动的,也即非gui的模式。但是原生的命令非常的冗长,Tuneupjs对相关的命令做了封装,采用了自己的runner,并且支持输出xml格式的测试结果,可以用于jenkins等持续集成工具。

./UI_Test/alexvollmer-tuneup_js-5a4346d/test_runner/runiCloudPlayIPhone  UI_Test/login.js

test-Result/-x –a 5

-x 表示输出一份xunit格式的xml

-a 5 表示启动instruments失败之后,重试启动的次数

   
次浏览       
 
相关文章

手机软件测试用例设计实践
手机客户端UI测试分析
iPhone消息推送机制实现与探讨
Android手机开发(一)
 
相关文档

Android_UI官方设计教程
手机开发平台介绍
android拍照及上传功能
Android讲义智能手机开发
相关课程

Android高级移动应用程序
Android系统开发
Android应用开发
手机软件测试
最新活动计划
LLM大模型应用与项目构建 12-26[特惠]
QT应用开发 11-21[线上]
C++高级编程 11-27[北京]
业务建模&领域驱动设计 11-15[北京]
用户研究与用户建模 11-21[北京]
SysML和EA进行系统设计建模 11-28[北京]

android人机界面指南
Android手机开发(一)
Android手机开发(二)
Android手机开发(三)
Android手机开发(四)
iPhone消息推送机制实现探讨
手机软件测试用例设计实践
手机客户端UI测试分析
手机软件自动化测试研究报告
更多...   


Android高级移动应用程序
Android应用开发
Android系统开发
手机软件测试
嵌入式软件测试
Android软、硬、云整合


领先IT公司 android开发平台最佳实践
北京 Android开发技术进阶
某新能源领域企业 Android开发技术
某航天公司 Android、IOS应用软件开发
阿尔卡特 Linux内核驱动
艾默生 嵌入式软件架构设计
西门子 嵌入式架构设计
更多...