赵高升(毅航)
项目背景
埋点自动化测试
埋点是在应用中特定的流程收集一些信息,用来跟踪应用使用的情况,后续用来进一步优化产品或是提供运营的数据支撑。
一个APP及其背后的系统发展到一定程度,主要功能基本定型,想要持续保持用户关注度和使用率,再靠持续增加新功能来吸引客户留住客户就很难了。一般都是通过运营、内容、优惠活动等方式来保持用户的新鲜感和吸引眼球。通过在APP中插入埋点,来统计分析用户的使用习惯、偏好等信息,进而帮助我们通过修改页面布局、创建活动等方式来运营。
埋点重要,但埋点是否有效及正确与否,通过人工测试的方式是效率很低的,常用的方式就是人工操作APP,再通过抓包工具查看是否有埋点接口和参数上报,或者通过查询日志来查看数据是否被保存成功。如果能有一套自动化测试工具,就能极大提升效率,解放人力。
实现埋点自动化测试的途径
埋点主要有页面曝光和点击等几种类型。通过接口测试的方式基本是没办法测试的,于是考虑到使用APP UI自动化测试方法。
APP UI自动化测试的成本很高,测试环境搭建复杂,且因页面经常变化导致用例失败率较高也是个头疼的问题,很多公司都是中途就放弃开发了。不过考虑到我们只做核心点位的测试,用例数不会太多,主站APP的核心点位在100个以内,这样写用例和维护用例的代价还可以接受。
设计思路
需支持Android和iOS两种手机
当前市面上比较流行的APP测试工具是Appium,且同时支持Android和iOS,所以做为首选。二期海拍客APP内置了录制和回放功能,上手容易且效率高,不足之处是复杂场景无法通过写代码做逻辑判断,适合写一些页面相对稳定,逻辑简单的用例。两者可互为补充。
需实现埋点数据自动管理
海拍客有埋点数据管理平台redpill,点位经常会有增删改,通过人工去及时检查这些有改动的埋点是不可接受的。于是在redpill应用中额外增加了获取核心埋点的接口,UI测试平台中添加定时任务,每天自动把核心埋点同步过来。
通过CS架构分离平台数据处理和与手机的交互
Web端和Server端是部署在容器中的,无法直连和操作手机,于是新增了Client端,部署在连接了手机的物理机上,一边可与Server端通过rest api通信,一边可直接通过命令行的方式操作手机。
其他主要功能见架构图
主要功能简介
用例管理模块
除了基本的增删改查功能外,主要介绍其他几个特性。
点位关联
每条用例有一个redpillPoints字段,存储需要验证的埋点,支持一条用例对应多个埋点。通过关联点位数据,界面上会根据不同颜色区分点位是否正确有效,比如蓝色表示有效,红色表示无效(可能是手工填写错误,或者该点位已从埋点数据平台中移除),这样就能够明显看出问题,进行用例修改或删除。
关联APP录制的脚本
对于平台存在的由APP录制并上传的脚本,用例可以与之关联,关联后的用例被标记为“录制”类型用例,执行时将不适用Appium,而是使用APP自带的回放功能。
选择关联的脚本时,会显示已存在的关联关系,防止关联错误。如下图中有3条用例已被用例关联,其他脚本未被关联。
用例在线调试功能
本地写好的用例支持以文本方式保存到平台上,平台提供了单条用例调试功能,便于使用线上环境和连接的真机进行调试验证。
录制脚本管理模块
平台二期对接了使用海拍客APP内置的录制回放功能。平台需要能接收保存APP录制后传上来的脚本。
APP录制、回放、上传功能
对测试账号提供了开启录制功能的开关,点击开始记录进行录制,点击视频回放可回放APP内保存的上次录制的脚本。点击上传脚本可传送到UI测试平台。
录制脚本管理功能
录制的脚本,本身没有功能描述信息,可以自行添加描述信息说明脚本对应的功能,便于关联用例。
除了用例可以关联脚本外,脚本管理界面也可以反向关联用例,并即时展示关联关系。
点位管理模块
点位自动同步
一个APP有上千个埋点,每天都有可能变化,人工维护基本是不可能的。平台添加了定时任务功能,每天自动从埋点数据平台同步海拍客APP的核心点位。
如果有些特殊的点位不属于核心,但根据需要仍需要覆盖用例的,平台也支持手动添加,且添加时只需要填写埋点字段,其他字段如模块、点位名、点位类型、是否核心等信息会帮你自动获取并保存。
点位自动匹配关联的用例
界面会显示该点位被哪些用例覆盖,这样就能清晰的知道还有哪些点位需要补充用例。
点位是否上报成功查询工具
写用例调试过程,经常需要验证埋点是否上报成功,通过抓包工具或去阿里云sls上搜索日志都比较麻烦。平台提供了个简单的查询工具。
APP安装包管理模块
安装包数据自动同步
平台对接了APP打包平台,可准实时自动发现新的安装包,由Server端对数据进行保存和初始化,再由Client端进行下载、安装,调用Server的接口触发自动测试任务。每个步骤的状态都有记录,便于跟踪测试进度。
安装包数据自动安装
Android手机:安装方式比较简单,Android手机安装APP不区分测试版本和线上版本,直接通过“adb install -r $filePath”静默安装即可,可保留账号登录的授权状态。
iOS手机:因为iOS release版本只能通过App Store下载安装,如果需要完全自动化测试,只能安装test版本。需要在连接的物理机上安装ideviceinstaller工具,使用“ideviceinstaller -i $filePath”安装下载的ipa文件。
自动安装后,会通过钉钉消息通知测试人员。
场景管理模块
关联执行测试对应的物理机和手机设备
设备Host:对应物理机的ip,平台支持部署多套物理机环境,每台物理机都可以连接若干台Android和iOS手机
设备选择:支持使用指定的手机执行测试。后续会添加设备管理模块,可支持自动选择可用的空闲手机执行测试
执行设备:手机序列号,主要用途是用adb连接手机进行版本安装,及appium需要用其执行测试
设备ID:同盾设备ID,埋点上报数据的一个字段,用例执行完成后需要通过设备ID来查询日志和验证埋点上报结果
支持定时执行测试任务
使用cron表达式,可设置需要何时自动执行测试任务。
执行完成后,通过配置的钉钉机器人,自动发送报告给测试人员。
失败重试机制
UI自动化测试,经常会因为网络问题导致预期的页面还未加载完成,或者获取页面元素时失败,为了增强整个测试任务的稳定性,对每条用例都添加了失败重试功能。默认设置3次,只有该用例连续三次都执行失败时才标记为失败。
场景关联用例
场景描述了需要连接哪台物理机、哪台手机、何时来执行测试,实际执行的是用例,场景需要关联一批跟该场景相关的用例。
如下图,已关联的用例会置灰,未关联的用例可供选择。
执行场景测试
若某个场景配置的手机是空闲状态,该场景显示执行按钮,点击可开始测试。测试完成后,会显示用例数统计。
若某个场景正在执行中,其状态是running,此时点击停止可终止测试。
设备在线状态实时监控
真机测试和虚拟机比,相对不太稳定,可能因为物理机ip变化、误碰了数据线导致连接断开、手机系统原因导致连接不上等原因,无法测试。此时就需要有个能监控设备在线状态的功能。
对于Android设备来说,使用adb devices能方便的知道设备连接状态,但iOS手机就没这么直接能通过命令行的方式去检查。而且还有个问题,真机测试需要在物理机上部署Appium、xCode等多个程序,这些程序如果有问题,也会导致无法测试。
想到Appium本身有连接真机的功能,能否利用这个特性来开发一个自动监控的工具呢?
利用目前的场景执行功能,稍加改造就能达到。
- 创建一个专门用来监控设备状态的特殊场景,该场景不需要关联具体的用例
- 根据场景名判断如果是设备监控场景,则转入checkDeviceOnline,利用AppiumDriver进行连接,连接失败则表示设备离线。
- 设备离线会实时发送通知
执行记录模块
场景执行记录和用例调试记录都聚合在一个页面,便于查看
执行记录页面也显示执行状态,支持中断测试
执行记录用例详情页交互体验提升
- 详情页会间隔5秒自动刷新结果
- 自动展开当前执行中用例的执行步骤,用例执行完后自动收缩并展开下一条,在不需要任何操作的情况下就能够观察详细的用例执行情况。
使用Groovy存储和执行用例
Appium框架支持用Java或Python语言编写和执行用例,用例代码以文件的形式保存在代码所在工程中,测试时编译运行是可以的。但如果需要能让测试平台保存和维护这些用例代码,同时通过平台能触发这些用例执行呢?我们引入了Groovy。
封装用例相关的变量及用到的工具类
用例相关的变量:
场景ID(VK_SELF_SCENE_ID)、
用例ID(VK_SELF_CASE_ID)、
场景执行ID(VK_SELF_EXEC_ID)、
用例执行ID(VK_SELF_CASE_EXEC_ID)
用到的工具类:UIUtil(封装了Appium对APP的操作方法),LogUtil(封装了日志操作),PointUtil(封装了埋点验证方法)
//获取GroovyShell对象的context
Binding binding = context.getGroovyShell().getContext();
//封装用例用到的变量
Map<String, Object> self = (Map<String, Object>) binding.getVariable(GroovyEnv.VK_SELF);
self.put(GroovyEnv.VK_SELF_SCENE_ID, context.getSceneId());
self.put(GroovyEnv.VK_SELF_CASE_ID, context.getCaseId());
self.put(GroovyEnv.VK_SELF_EXEC_ID, context.getExecId());
self.put(GroovyEnv.VK_SELF_CASE_EXEC_ID, context.getCaseExecId());
//封装用例用到的工具类
UIUtil uiUtil = new UIUtil(context.getDriver());
binding.setVariable(GroovyEnv.VK_UI, uiUtil);
binding.setVariable(GroovyEnv.VK_LOG, new LogUtil(self, testExecLogAO));
binding.setVariable(GroovyEnv.VK_POINT, new PointUtil(context, loghubFacade));
执行脚本
通过Groovy提供的InvokerHelper.createScript执行用例对应的脚本script
private void exeGroovyShell(GroovyShell groovyShell, String script) {
Class clazz = testScriptAO.parseScriptClass(script, groovyShell.getClassLoader());
Object validResult = InvokerHelper.createScript(clazz, groovyShell.getContext()).run();
AssertUtil.isTrue(validResult == null || ((Boolean) validResult), "校验失败,结果为false");
}
用例示例
执行Groovy脚本时就可以通过绑定的Java对象做各种操作,并把结果返回给测试平台。
//通过封装的ui对象启动APP
ui.getDriver().launchApp();
//通过封装的point对象执行等待
point.sleep(8000);
//通过封装的logger对象采集日志
logger.log("开始测试");
logger.log("点击首页奶粉行情");
//通过封装的ui对象操作APP
ui.findXpath("//android.widget.TextView[[@text='奶粉行情']",5).click();
point.sleep(3000);
ui.findXpath("//android.view.View[@text='下单']",5).click();
//通过封装的point对象对埋点进行验证
assert point.checkPoint("6.13.10.1.3"):"点位上报异常";
ui.getDriver().closeApp();
logger.log("结束测试");
主流程简介
使用APP自带的录制回放功能主要流程
创建用例
- Web端创建一条空用例,可以不需要填写脚本内容。
- APP端开启录制功能,点击开始记录按钮,按照用例步骤手动操作各个功能,点击停止录制按钮,录制的脚本就会保存到手机中。此时点击视频回放按钮,可以检查录制的步骤是否正确。
- APP端点击上传脚本按钮,调用UI测试平台的脚本接收接口,即可保存脚本到平台数据库。平台web端录制脚本页面会显示刚上传的脚本。
- Web端可以在录制脚本页面关联第一步创建的空用例,也可以在用例管理页面关联上传的录制脚本。
执行任务
- Web端触发任务,逐条执行用例
- 物理机上部署的Client端接收到用例执行请求,使用ios-deploy -L -b ./Payload/*.app --args "bootstrapTime=$1 startTime=$2 endTime=$3 taskId=$4"发送指令给APP
- APP端通过传入的参数,在内部查找脚本,如果找到则直接回放;如果APP重新安装过则脚本会被清空,APP会调用Server端提供的接口重新拉取脚本,再开始执行。
- APP端用例执行完成后,会调用Server端提供的执行结果接收接口回传数据。平台通过taskId匹配执行的用例及执行结果。
新版本检测
- Server端定时从oss查询是否有新版本,如果存在,则在数据库插入一条初始化的记录。
- Client端定时调用Server提供的接口,判断是否有新版本需要下载,如果有新版本,则使用wget下载新版本到物理机。如果是iOS安装包,还需要解压。
- Client端调用Server提供的接口,判断当前该设备是否有任务执行中,若设备是空闲状态,Client执行adb install进行安装,并标记这条记录为已安装。
- Client端定时检测最新的版本记录,如果是已安装未测试状态,则调用Server提供的忌口自动执行一次测试任务。
主要功能流程图
使用Appium进行测试主要流程
创建用例
- Web端创建一条用例,需要填写脚本内容。
执行任务
- Web端触发任务,逐条执行用例
- 物理机上部署的Appium收到请求,连接手机执行用例。Android手机额外需要部署adb环境,iOS手机需要额外部署xCode和WebDriverAgent环境。
新版本检测
- 基本与录制回放流程一致,不同点是iOS下载后不需要解压一次。
主要功能流程图
遇到的问题
iOS对录制的脚本进行回放耗时长的问题
iOS使用APP自带的录制工具录制脚本后,通过命令ios-deploy -L -b ./Payload/*.app --args "taskId=$1"可以回放成功。但是发现每条用例执行都需要一分钟以上。
经过分析日志和研究ios-deploy的参数发现,该命令会先从电脑拷贝并安装.app到手机,再执行测试。而最耗费时间的步骤就是拷贝文件,耗时达到50秒。
一次任务包含许多用例,是不需要每条用例都重新安装app的。于是首先考虑到的就是安装步骤和执行用例步骤分离开。
- 只执行拷贝和安装文件相对简单,只执行ios-deploy -L -b ./Payload/*.app ,不传任务ID等任何参数,App接收到命令后不进行回放即可。
- 再研究ios-deploy的参数,尝试出添加-m -d可不安装app直接执行后续回放操作,命令为:ios-deploy -L -m -d -b ./Payload/*.app --args "taskId=$1"
- 但还有问题,回放命令在app已经启动的情况下没有做任何操作,完全没反应。发现只有当app的进程是退出状态时才能把app启动并执行测试。
于是沟通后由开发在安装命令和执行用例命令上,均添加exit=1参数,这样重新安装版本后app自动退出,每次执行用例后app也自动退出,不会影响下条用例的执行。
Appium和App回放交叉执行出错
因平台现在是支持通过Appium执行用例和回放由APP录制的用例两种方式的,把两种用例放到一个任务中执行时,会偶现执行失败。后发现一种情况,当先回放用例后,再执行Appium的用例,就会提示session创建失败。
- 如果不管哪种方式执行后,都把session释放掉,就不会影响后续的用例,但对代码的改动稍大。
- 于是用了一个简单的办法,在执行任务前,对所有用例进行排序,先把Appium的用例全部执行完,再执行所有回放的用例。
结语
目前为止,埋点测试用例已完成了130+条,覆盖核心埋点90+个,有效的保证了埋点上报功能的有效性。
APP UI测试平台除了验证埋点是否正常上报外,还可以模拟真实用户针对其他的主要场景进行测试,是单元测试、接口测试的有效补充。平台支持的Appium脚本编写及录制回放两种方式互为补充,有效保证了用例编写的效率及执行的稳定性。
web端和APP端UI自动化测试是现在很多公司使用较多的UI测试类型,随着海拍客在小程序端的发力,接下来小程序端UI自动化测试平台也已规划并在开发中。