基于instruments-Automation的iOS应用UI自动化测试图文攻略

来源:互联网 发布:手机小视频特效软件 编辑:程序博客网 时间:2024/05/17 06:04

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失败之后,重试启动的次数

1 0
原创粉丝点击