在了解相关的Android App的自动化测试框架之前,先来看一个常用的自动化测试实例,这里先不讨论框架,主要是测试用户操作的模拟、执行结果的判断,以便获得对测试自动化的感性认识。
案例需求如下:QQ浏览器打开手机存储卡的文件,通过自动化测试获取其打开某一文件的响应时间,这里首先需要做细分,把需求拆分为几个关键点,即进入浏览器、文件打开操作、获取手机屏幕、截图分析、结果统计输出。自动化测试就是实现机器完成这些关键点的一系列操作,并且在脚本的实际运行中添加需要的业务逻辑判断,实现测试自动化。根据脚本的具体实现,整理出打开文件测试流程图,如图2-2所示。
图2-2 打开文件测试流程图
下载record和replay的脚本录制工具,有很多工具可以实现,比如本书中的MonkeyRunner、UIAutomator等,在这个案例里从网上下载record/replay可执行工具,直接复制至手机硬盘。
在PC上连接手机,打开adb shell命令,进入record工具存放目录,用shell命令运行record工具启动用户脚本录制,并给录制命名,如wps(打开wps文件)。工具运行成功之后,手工完成所需要的业务操作(如手机主页→选择QQ浏览器→文件目录→双击wps文件),操作结束后,按Ctrl+C键结束脚本的录制工作。录制脚本过程如图2-3所示。
图2-3 录制脚本过程
这里以Java语言进行测试脚本的编写作为示范,通过Java的ProcessBuilder类在PC上创造并启动一个cmd命令行的进程,并执行“adb shell replay”操作进行脚本回放,这样打开文件的功能就可以通过脚本完成相应的操作执行,如代码清单2-1所示。
代码清单2-1 实现浏览器中打开wps文件
public static synchronized void enterWPS() { ProcessBuilder pb = new ProcessBuilder("cmd.exe");//ProcessBuilder("/system/bin/sh"); // java.lang.ProcessBuilder: Creates operating system processes. pb.directory(new File("C:\\")); // 设置shell的当前目录。 try { Process proc = pb.start(); BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream())); // 获取输入流,可以通过它获取SHELL的输出。 BufferedReader err = new BufferedReader(new InputStreamReader(proc.getErrorStream())); PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter (proc.getOutputStream())), true); // 获取输出流,可以通过它向SHELL发送命令。 out.println("adb shell replay enter wps"); //打开WPS文件 out.println("exit"); String line; while ((line = in.readLine()) != null) { System.out.println(line); } while ((line = err.readLine()) != null) { System.out.println(line); } in.close(); out.close(); proc.destroy(); } catch (Exception e) { System.out.println("exception:" + e); } } }
这里的录制脚本存放路径需要读者在实践时进行更改,或者把录制的脚本放置在/system/bin/。
这里通过测试脚本完成了用户操作的模拟实现,但是正常的测试还需要结果的验证,需要编码脚本进行测试结果的判断,本案例可以通过截屏和图片分析wps文件是否打开成功,可以通过Google汉明距离相似度对比算法得到图片相似度来判定打开的结果,代码相对要复杂一些,后面的案例中也有类似的代码实现,这里就不再细述。
经过前面的一个简单的自动化测试案例,我们对Android的自动化测试有了一个感性的认识,很多有相关工作经验的测试同学也都会理解,这和PC的自动化测试思路是相通的,只不过所借助的框架不同,目前业界已经有很多成熟的开源Android端自动化测试框架,经常用到的框架代表有Robotium和UI Automator,各个框架可能在具体应用上有些不同,如有些偏稳定性,有些适用于Web应用,有些能支持跨应用,等等,但其主要思想是通过控件的位置、名称、属性等获取控件对象,并且对控件对象或者坐标模拟用户操作,测试同学通过这些控件的操作和状态变化来完成自动化测试的执行。图2-4里罗列了目前常用的测试框架,也是本书中实践的重点,后面的章节会详细讨论这些框架的原理及应用。
图2-4 目前常用的测试框架
这一小节讨论的自动化测试框架,是在实际项目中总结出来的且基本能运行的通用基础框架原型,它包括三个核心部分:一是如何获取坐标/控件并操作控件模拟用户端事件,二是脚本中的结果如何判断,三是测试结果报告的输出与展示。不管测试人员使用的是Robotium还是其他框架,万变不离其宗,通用原理都可直接图解为图2-5所示的这几个模块。
图2-5 Android自动化测试基本框架模块
自动化测试的首要条件是能够操作控件,最好像开发同学一样操作控件,如何实现呢?一种最常见的脚本录制方法,其主要思想是记录控件的坐标位置和发生的事件,通过回放脚本完成测试事件流,像Monkey Runner框架就提供比较方便的录制回放功能;另一种方法就是通过工具(比如:源码、UIAutomatorviewer等)获得测试界面的控件布局,找到目标空间的ID、名字、描述或者位置信息。测试框架可以通过这些信息得到控件对象,并对控件对象执行一系列事件操作像Robotium、UIAutomater等,这个阶段理解为测试的动作执行。
当然对于有跨应用App的控件操作会受到Android进程安全限制,这对于跨应用的操作是一个难点,举个简单的跨应用例子,在测试一款App应用时,它的某个功能会调起系统相机拍照,那这个功能就会涉及跨应用了。像Robotium就无法调用系统的一些INPUT事件完成跨应用的控件操作(其实Robotium从Android 4.3之后开始支持UIAutomation框架,理应可以支持跨应用的),基于Robotium框架的测试脚本跟被测对象需在同一个App或者可以相互访问,一般要求重新签名打包。所以在选定框架时就需要考虑相关的权限问题,当前可以直接支持跨应用的框架有MonkeyRunner、UIAutomater等,后面的章节会详细给出主流框架的分析和使用建议,这里不再细述。
自动化测试中非常重要的一个环节就是测试步骤的验证,如何能不进行人工干涉而去正确并且自动地检查验证点,这是自动化测试环节中的一个关键点,有些可以借助测试框架的截图对比(像MonkeyRunner)直接完成结果输出,但在实际项目中会有很多具体业务,验证点会比较困难,不同的案例可以用拆解和辅助的验证方式来完成。比如播放器播放视频时,如何验证视频播放成功,这里就需要设定很多验证点。若播放的时间≥0,可以直接获取video标签里的current time来判断;若是功能测试,则有两种情况:①播放时有画面,可以直接截屏获取;②播放时有声音,可以获取声卡捕捉声音的数据,分析波形文件,但实现起来难度就比较大,后面的章节中有具体的案例介绍。基本结果验证的方法概括如下。
(1)截图对比。对于GUI的测试,一般会采用截图方式,但最简单的截图也需要考虑到很多附带的条件,大部分框架会有此功能(没有的话也不要紧,可以直接用系统自带的screencap或者其他工具配合使用),但它每秒能截取多少张图片?能否满足我们需要的图片判断条件?截取的图片如何做对比,对比的参照物是什么?做性能测试时,我们的工具是否会影响性能?要不要选择拍照工具而不影响性能?获取图片的速度能否满足性能测试要求?对比的参照物又如何设定?这些信息在每个具体的案例中又会衍生一系列新的问题。就一个具体的案例来说,浏览器打开网页的首字响应时间测试中,对比参照物可以是一张空白的图片,把指定的像素区域与这张空白图片做对比就能判定结果;而浏览器播放视频时的响应时间测试时所截取的图片,就不能这样判断,因为很多视频会在播放之前就显示一个关键帧的图片。为什么会这样?本书后面的章节会针对一些典型业务进行由浅入深的案例分析与工具设计,会用到基本框架但不局限于此,还需要测试人员更多的对工具和框架的配合应用与改造能力,完成系统性的业务测试需求。
(2)控件对比。Android UI自动化测试最直接面对的是应用程序界面,界面上存在按钮(button)、文本框(textView)等,我们都称之为控件。有些应用程序的执行逻辑直接体现在控件的状态显示上,如按钮状态的变化、文本的改变等。常用的自动化测试工具Robotium和UI Automator都提供了获取应用控件的接口,当执行完一定的自动化测试逻辑后,可以将获取控件上的信息与预期的信息进行对比,判断测试结果是否通过。比如测试一个计算器程序,执行3乘以5的测试用例后,可以在结果输出框得到输出的值(result),用框架的结果与预期的结果做对比:assertEqual(result,15),判断这个测试用例的结果是否通过。
(3)日志分析。日志分析可以作为一个辅助结果判断,比较适合在集成测试阶段做的一些接口、稳定性和性能测试,因为这个阶段产品的日志一般处于打开状态,测试同学可以很方便地收集到日志关键字信息。比如在进行稳定性测试时,可以直接把所有日志输出至文件,在测试执行程序中加入简单的文本处理,对日志信息进行分类检索,像在Java层出现crash时,可以搜索日志中的FATA EXCEPTION/ANR等关键字,提取后面的几十行日志信息进行筛选处理。日志使用的更高进阶是日志埋点,对需要测试的接口埋入上报信息,一旦测试执行(用户使用)到该点,启动上报接口(或者SDK)送至指定的路径,就可以直接准确地看到测试结果,一般在灰度发布后这些日志信息会被关闭,总体上不会影响产品的功能。但日志的使用也有一些局限性,比如测试同学对日志tag不熟悉,需要先了解代码信息等,还有竞品对比测试时因为拿到的竞品包都是混淆的正式发布包,没有关键日志信息输出,可能需要用其他的手段来判断结果,比如控件对比(当然高手例外,他们可能会使用逆向工程,反编译再注入需要的log)。
报告展示一般是指给出整个测试的结果信息汇总并进行简单的分析,测试结束后直接输出预警和初步的数据报告,以邮件或者其他形式直接周知项目参与人员。如果执行较大的项目测试,就可能需要多维度、多指标地对待测应用的测试数据进行整合,要求测试同学对平台有较高的设计能力,比如平台需要实现产品中核心业务的性能benchmark测试结果及分析,BVT(Build Verify Testing)测试中发现的问题预警,功能自动化测试脚本中的BUG自动提单,等等。有些自动化测试框架里也带有简单的测试管理平台,根据业务类型决定这些平台是否能满足需求,有的产品需要平台支持多业务的横向与纵向对比信息、竞品的评分信息等,这时就需要做二次开发,形成一个系统性的测试管理工具,也即前文中说的测试的管理平台框架。