解放程序猿(媛)的双手—iOS UI自动化测试
前言
随着移动互联网时代的蓬勃发展,移动终端的自动化测试也在业界日益活跃,总体来看在Android平台上的自动化工具和实践比较多,但是说到iOS平台无论从自动化工具的数量还是质量上就陡降了。究其原因,无外乎是iOS系统的封闭性,加上相对Android用户的数量少,导致对这个平台系统的自动化进展缓慢,据笔者了解到的情况,很多iOS平台的测试人员还处于纯手工测试模式,自动化程度和Android平台无法相论,更别提和PC端相比了。
然而越是困难的事,越是研究的少,就越发有挑战性。有挑战性的事大多又会带来不菲的收益,如果能在iOS上做出大规模可持续运行的自动化测试,那么对iOS的测试演进无疑是一次大的推动。
手机QQ浏览器(iPhone)测试小组的同学在比对和实践了业界已有的iOS自动化工具,总结提炼了对比,如下表所示。
表中没有列的内容是稳定性,实践中看来上述除UITesting之外的工具稳定性都相对一般,会出现自身框架导致的各种闪退,以及性能越来越差的问题。
因此iOS平台上除了Monkey测试采用了自动化方式,以及部分性能测试轻度使用了一些自动化工具,大部分功能测试还是依赖于人的操作。移动APP更多的场景是面向用户,面向界面操作,UI测试就非常直观和重要,因此我们致力于寻找一种稳定性高,且易于掌握的自动化测试工具。
久旱逢甘霖
苹果公司在2015年推出的Xcode7中引入了UI Testing工具,该工具配置相对简单,还支持录制回放功能,运行速度很快,测试代码也可以调试,使用OC作为脚本测试语言兼容性较好,支持UIWebView,支持截图功能,最重要的是稳定性高。
其实选用一个工具,最看重的就是稳定性和可调试。可调试性不必多说,就是刚需。而很多工具没有被很好的应用就是因为稳定性较差,UI Testing是苹果开发的,和被测程序的兼容性好,实践证明,确实能够彻夜稳定运行脚本,未见因工具自身原因导致的闪退。性能方面也影响较小,后期发现一些截图操作会有一点点影响速度,但是整体运行还算良好,没有明显变差。
在大量工具都无法在iOS上施展拳脚时,UI testing姗姗而来,带给我们惊喜和希望。
必经之路
工具是好工具,但是如果想在项目中实际应用起来,还是会遇到许多问题和困难,这里给大家分享下我们的磨合期。
第一个挑战:门槛要低
要使用这个工具,扑面而来的问题就是怎样快速上手,也即学习成本和投入产出比的问题。地球人都知道OC语言并不是一种容易快速上手学习的语言,加上底层是XCTest接口,录制后能看到的实现就是下图这样的,看着很凌乱有没有?
因此我们需要做的就是进行封装。封装包含接口封装和特殊控件的封装两部分。
接口封装有两层,一层主要处理弹窗,异常,超时等待,手势处理等操作,二层是把控件类型和手势合二为一,提供脚本使用。实际脚本编写者就可以直接调用,命名和使用方式都具备高可读性和可用性,另一方面底层的接口修改,不会影响到上层的函数表现形式,这对自动化用例编者来说也降低了门槛,不必关注底层接口的细节变动。
例如上图所示,浏览器多窗口界面向左滑动就可以删除页面,可以选择删除第一个页面。将这个操作封装起来如下图所示,后续使用即可直接调用该函数,只需传入对应参数即可。
特殊控件的封装。一般应用于控件无法寻找到对应的唯一标识,或者层级比较多的情况下,通过录制的方式,读取控件属性,将其打包成一个整体,再后续使用时可以直接调用。
例如上图所示是浏览器多窗口的管理界面,右下角有个返回按钮,通过查看xml结构无法获知唯一标识,通过录制的方式确定控件结构。对录制的内容进行加工处理后,封装为特殊控件,如下图所示,存放于指定文件内,方便后续使用。
例如上图所示是浏览器多窗口的管理界面,右下角有个返回按钮,通过查看xml结构无法获知唯一标识,通过录制的方式确定控件结构。对录制的内容进行加工处理后,封装为特殊控件,如下图所示,存放于指定文件内,方便后续使用。
使用中,可以直接调用已经封装好的接口,每个接口都包含一个或者多个固定参数,和一个可变参数。固定参数按照所给出的类型传值就可以了,而可变参数,需要我们按照一定的格式进行参数传递。举例可变参数格式:VariableParameter:@” StepName=切换小说书架为宫格模式&LogLevel=WARN”,其中多个参数使用&符号连接,每一个单独的参数使用KEY=VALUE的形式。
第二个挑战:灵活编译
在自动化测试中经常遇到的问题是配置环境和单例运行的问题。
首先环境配置会不会很复杂,很大程度上制约着使用者的使用频率。相比较APPium的复杂设置,UI testing的配置就简单明了多了。
- 1) 添加服务端配置。需要将UITestServer.xcodeproj添加到目标工程的thridparty文件夹下面。UITestServer.xcodeproj是自研服务器,主要功能是:嵌入被测试APP中,实现端口监听,服务开启,消息获取,消息处理,各种事件功能(截屏,获取被测试APP日志信息,获取内存,cpu,网速,流量等功能),还可以扩展。
- 2) 配置被测APP。需要在被测APP的主加载函数中加入监听服务端口。为了不影响发布版本的使用,我们采用DEBUG模式。
- 3) 配置QBUITests(名字自定义)组件部分,该部分主要是我们的自动化测试框架部分,包括各种自动化组件,自动化脚本,配置信息等。需要将自研文件夹UITestUtils,SystemResources,SpecialElement,ScreenShot,Log,ScriptInterface全部拖动到QBUITests Target下面。
经过上面的配置,框架配置基本完成。
接下来看单例运行问题,如下图所示是小说模块的自动化脚本头部,包含开头的初始化操作,直接可以运行单例“test311001”,也可以进行正常的调试,也可以指定运行全部用例或者部分脚本。
第三个挑战:结果可查
做UI自动化也好,做监控或者准入测试也罢,最终都要有个输出结果。一般来说对于自动化测试的结果要具备两点:截图和日志。截图可以时时截图,也即可以让自动化设计者或者测试人员随时获取想要的结果图片,也可以设置发生错误时自动截图。日志系统就要详细记录操作过程,在确认问题是可以一步步通过日志追溯。
UI Testing除了控件识别和简单操作外,并没有提供屏幕截图功能,我们需要自己完成屏幕截图功能,而且还要能够在各种封装好的函数中灵活使用截图功能。如下图所示,在自动化用例脚本中使用函数时,可以随时在可变参数里设置LogLevel=WARN,即可在操作执行过程中进行截图。
当然在程序运行异常或者元素找不到的时候也会自动截图。这些截图的操作都默认放在封装函数里了,使用者不必单独设置。
系统日志的获取分为两种,一种是过程中的操作记录,一种是内存之类的性能日志。日志的获取需要用到跨进程通信,前者主要是记录自动化用例执行过程中的操作步骤和截图信息,后者主要是获取设备的系统性能数据,例如内存、CPU、网络等。生成的本地log如下图示。
最终整合到日志系统(后文讲解)下图。
发展壮大
在初次磨合后,基本上有了本地单机运行自动化的能力,但是要真正对项目有所帮助,就必须有比较好的运行方式和结果展示,因此对这个自动化的研究还需要进一步发展。
第一、整体架构
首先要对基于UI Testing的自动化整体架构有个了解。如下图所示,使用系统提供的XCTest接口、消息处理、驱动模块、系统资源获取,在中间层进行封装,包括控件调用封装,特殊控件封装,截图模块,日志处理模块。这些内容在上文都有讲述。
关于整体架构的内容在图中的最上层。一个是集成在XCODE里边的自动化运行框架和脚本,另一个是分析log日志的自动化log日志分析系统。如上图所示是在基于控件调用驱动的基础上,使用自动化脚本和配置文件完成自动化测试的工作。然后使用日志分析系统,包含日志分析、展示、邮件等,给到项目团队以完整的可视化报告。
第二、日志系统
日志分析系统是使用python开发的系统,所以其是独立于XCODE框架的,当自动化脚本执行完成后,需要把log日志传到分析系统下,然后使用该系统分析出日志,方便用户查看脚本错误信息,分析定位问题。
自研的日志分析系统可以将每次运行的日志升成html格式,如下图所示。
详细脚本日志介绍,如下图所示。
步骤详细信息,如下图所示,一般只需关注红色错误信息。
通过该日志系统就可以获得良好的可视效果,方便问题追踪和回溯。
第三、持续集成
功能的自动化测试,最终的期望还是能够随着版本进行持续测试。有些比较好的应用场景:准入测试、开发自测、回归测试。不管是哪种应用场景,最佳方式都是将自动化框架合入主线,方便项目组的不同成员随时在任何开发机上运行。
但是!这里是iOS平台,有一种忧伤叫做苹果审核不通过,有一种致命拒绝叫做查出私有API。虽然配置简单,合入主线跟随代码构建看起来不是个难事,但是难就难在需要打包升成版本的时候不能将自研部分的服务器代码编译进去,这部分会经常含有私有API(一些功能的实现必须要用到私有API)。因此我们采用了动态关联的方式,在主函数所在的文件中加入下图所示内容。既能顺利将框架合入开发主线,又可以在编包发布时不编译这部分代码。
实践效果
凡事都讲究个投入产出比,前期做了大量的预研和实践工作,那究竟在项目实践中能发挥怎样的效果呢?接下来为大家展示一下。
第一、部署情况
目前手机QQ浏览器(iPhone)项目上,已经采用这种基于UI Testing的自动化测试方法进行BVT建设,每天晚上测试白天提交到主线的最新代码,保障主线质量稳定,并为第二天早上的提测包做一个准入测试。
在部署时考虑到版本迭代以及UI变更大的问题,主要是在浏览器基础FT上进行了大量自动化测试部署,还对用户访问的TOP页面进行检测,如下图所示。确保合入主线的代码不会影响到浏览器基础功能,及时发现问题,避免提测后再暴露问题,造成时间和资源的浪费。
发现有问题的地方还可以进入详情查看,如下图所示点击测试LOG链接。
另外对一些浏览服务SDK也进行了自动化测试监控,如下图所示。
第二、投入产出比
投入产出比的问题,要看两个方面,好比天平的两端,一端是投入,一段是产出。得产出重过投入才是一个好项目,值得长期运营。
如上图所示,我们的投入成本可以分成两块,分别是一次性成本和线性成本。我们以应用于实际项目的iOS上的BVT来说明。
一次性成本: 框架研发(1个人2个月)+配置和部署(4人0.5天)+学习成本(3人1天)。这个一次性成本主要消耗在框架的研发上,以及测试人员的初始培训上,后续只有新加入测试人员才会增加这个成本。事实证明在一次性成本上的投入非常值当,好的框架可以保证提高后期运维阶段的稳定性和使用的简易性。
线性成本:自动化用例编写(平均14分钟/条),每日需要维护的成本(8分钟左右)。线性成本随着时间的推移可能会产生变化,例如自动化用例随着测试人员的熟练程度单条用例的编写时间会减少,每日需要维护的成本随着用例数的增多和需求变动增多会增高,这些都在预期范围内。
产出方面,我们的评估分为客观和主观两方面。
客观:
- A. 前置bug暴露时间。BVT每日运行,因此总会提前于正式提测前暴露问题。目前是部署在主线上,主线的提测频率大约40天能提测10次左右,也即平均下来差不多4天才能提测一次,一旦BVT发现问题,平均能前置三天发现。由于数量都是平均来算,因此只能大约估值,例如发现了29次问题,前置时间就是29*3天=87天。
- B. 减少提测拒绝次数,节省人力时间成本。由于BVT里的自动化用例全部是基础核心用例,一旦出现运行问题,就是不符合准入测试标准的。在没有BVT的时代,提测前都是开发手工自测和测试手工验证的方式进行,一旦发现不符合测试条件的bug,就会打回,这种情况下就会消耗不少的人力和时间。有了BVT后,这种情况大大减轻,开发可以自己运行自动化脚本做基础功能自测,测试每日监控也在运行检测。 目前手工运行一遍自动化的用例大约需要200分钟,机器在夜晚用60分钟就搞定,每个版本的拒测次数大约是2次左右。如果用狂野的算法应该是节约了200*2=400分钟,低调点的算法,应该是“从开始测试到发现导致拒测的bug所用的时间”拒测次数,但是这个数据暂时来说不可考。
主观:
为什么要放上主观收益呢,因为客观上节省的时间,在主观上还要有个内心“更淡定”了的感受。总体来说BVT的部署,大大提高了测试在项目组的影响力,从此iOS上的测试从纯手工迈入了新时代,每日版本质量也有了持续稳定的检验,全项目组的内心也更加淡定了。
第三、运营数据
如下图所示是每日发现问题的统计,金色的线是确认是bug的数量,蓝色的是BVT每日报出来的问题,可以看出两线基本重合的点比较多,这反映了误报率相对维持在一个比较低的水平。
发现的问题中主要分为三类,如下图所示,分别是纯误报(因为脚本的稳定性导致的)、UI变动(包含被测元素变动、需求变更)和真实bug。如下图所示是统计的两个月的数据,可以发现UI变动导致的问题占总发现问题的比例相对较低。这些数据是在没有与开发约定代码规范的时候,随着后期的合作,这部分UI变动导致的问题中的元素属性变动问题将会降低,但是纯需求变动的问题还是保持一定的比例。
写在最后
最后,还是需要和读者交流下这个自动化方案的问题和未来。
目前已知的问题是基于UI Testing的自动化测试方案需要的硬件要求。联机操作或者模拟器,得有iMac,操作系统得是OS10.10.5及以上版本,Xcode版本得是7.1及以上,Python版本2.7及以上,内存2G及以上为佳。没有这个配置,恐怕无法良好的运行自动化测试。
还有一个性能问题,已经封装好的工具脚本总数理论上控制在1000个以内,可稳定运行10小时以上。在这稳定运行的10小时内,可以满足我们的自动化需求,但是时间超过几个小时后,还是会有些许减速,可以通过修改底层消息传递机制,提升传递效率,减少截图运行时间,也是今后自动化测试继续优化的方向。
未来呢,继续扩大脚本覆盖范围,对特殊控件做封装,对函数的灵活性做优化,降低自动化测试的准入门槛,提升自动化测试的效率,期待更多同行业的牛人一起探讨!
本章完~