第10章 自动化测试基础综述
软件测试是一项无比艰苦的工作,需要投入大量时间和精力。另外,使用等价类划分、边界值等技术进行测试用例的设计,需要承担风险,因为减少了测试用例的覆盖,选择对某些测试用例进行筛除。
一款实用的软件测试工具对软件测试工程师来说非常重要。
本章介绍目前比较流行的自动化测试工具,如无特殊说明,本章所介绍的都是基于Android系统的自动化测试工具。
1.自动化测试工具的优点
大多数软件在发布之前要经历多次重复代码、测试、修复的过程。如果要测试某项特性,也需要多次执行测试。如果需要执行数千条测试用例,在有限的时间里只能够把所有测试用例执行一遍,要想执行多次是不可能完成的。
这时就要考虑是否可以通过比手工测试更为有效的手段,即使用自动化测试工具帮助解决问题。
自动化测试工具的优点如下:
(1)速度。使用自动化的手段,可能比人工测试快10倍,甚至百倍。
(2)效率。这里的效率指的是测试工程师的效率。当测试工具执行测试用例时,测试工程师可以做其他事情,比如制定测试计划、研究新的测试手段。
(3)持续。机器不会疲劳,它可以持续工作。
小Q:既然自动化测试工具有如此多的好处,那它是不是可以取代测试工程师呢?
M博士:这肯定是不可以的,自动化测试工具只能帮助测试工程师更好地完成工作,但不可以代劳所有工作。
2.目前市场上的主流自动化测试工具
在介绍主流自动化测试工具之前,先考虑什么是好的自动化测试工具。
1)稳定性好
自动化测试工具必须能够长时间运行,以便通过长时间的重复测试发现待测试软件的内存泄漏等问题。
2)移植性好
自动化脚本如果移植性不好,就会导致每次移植的成本非常高。在Android的自动化脚本里,需要尽量少地使用坐标进行控件定位,因为一旦移植到分辨率不同的手机里,所有用坐标进行控件定位的脚本全部都需要重新修改。
3)运行效率高
页面的跳转和页面的定位需要准确,现在很多自动化测试工具无法做到这一点。
4)容易上手
界面友好、人性化,所提供的接口容易理解和掌握。
5)开发效率高
开发效率高意味着自动化测试工具的封装程度高,比如一行脚本只有一条语句,却能完成一个特定的操作,如“打电话给10086”。这条语句封装了“打开语音通话界面”“输入10086”“按拨号键”3个操作,但使用人员完全不用理会具体封装的操作,只需要了解使用“打电话给10086”这句话就可以完成打电话这个操作即可。
3.准备工作
接下来需要用Android模拟器或真实的Android手机进行自动化测试工具的学习,因此先介绍如何开启手机的开发者模式。目前为了防止用户误操作,很多厂商把手机的开发者选项隐藏起来,需要通过特定的步骤开启。如果不开启开发者模式,即使手机连接了计算机,也无法和自动化测试工具连接。
将手机连接到计算机,安装好驱动。接下来在手机上进入如下操作,开启USB调试功能:
(1)点击手机的“设置”图标,进入“设置”界面;
(2)点击进入“关于手机”界面;
(3)连续点击“版本号”,直到提示“您现在处于开发者模式!”;
(4)返回“设置”界面,可发现在“关于手机”选项上方多了一个“开发者”选项;
(5)点击进入“开发者”选项,选择开启,同时开启“USB调试”功能。
到此,手机上的USB调试功能已经开启。
接下来,确保计算机的环境变量里已经配置好了Android的SDK目录。关于如何配置Android SDK环境变量,前面已经介绍过,这里不再介绍。
最后确认环境没有问题后,打开计算机的命令行窗口,输入“adb devices”,如图10-1所示。

图10-1 检查手机是否已连接
注意,这里的“emulator-5554”表示目前连接到计算机上设备的Device ID,不同的手机显示的值是不同的。
此时,手机已经跟计算机成功连接,并能使用自动化测试工具进行测试。如果没有找到设备,请仔细检查上面的步骤,确保每一步都没有出现问题。
10.1 Monkey工具
10.1.1 Monkey概述
Monkey是Android的稳定性测试工具。软件每次发布新版本,都必须先通过稳定性测试,要想通过稳定性测试,先要通过Monkey测试,可见Monkey测试的重要性。开展Monkey测试相对快速且有效,只需要一行命令、几个参数就可以启动Monkey测试,这表明了Monkey测试的高性价比。
Monkey就是“猴子”的意思。Monkey测试的过程就像让一只灵活的猴子乱敲计算机键盘来进行测试。
小Q:让一只乱敲键盘的猴子来测试一个软件,难以想象会发生什么。
M博士:所以,只有一个软件的稳定性达到相当高的程度,才可以通过这种随机的测试。Monkey测试对软件的稳定性要求非常高。
10.1.2 Monkey的基础使用
Monkey是Android的一个命令行工具,可以运行在模拟器或实际设备中。它向系统发送伪随机的用户事件流(如按键输入、触摸屏输入、手势输入等),实现对正在开发的应用程序的稳定性测试。
打开Android模拟器,或将真实的Android手机连接计算机。
(1)打开命令行窗口,使用“Win+R”快捷键或是选择“开始”→“运行”命令,在输入框里输入“cmd”,然后按Enter键。
(2)输入“adb shell”,按Enter键。
(3)输入“cd/system/bin”,按Enter键。
(4)输入“monkey”,按Enter键。
也可直接输入“adb shell/system/bin/monkey”,然后按Enter键。
这时,Monkey以无反馈模式启动,运行结果如图10-2所示。
Monkey并没有动作,只是显示了一些帮助信息。

R10-1 monkey无反馈模式启动
现在看一下图10-2中“usage”后面的内容。“[]”里的参数都是可选的,也就是说不选也可以启动Monkey测试。最后一行的“COUNT”是不在“[]”里的,是个必选的参数。前面的命令行里并没有COUNT值,因此,需要把COUNT值加进来,其运行效果如图10-3所示。
这里涉及一条Monkey的命令行代码:

这里的<eventcount>指的是随便事件的数量。

图10-2 Monkey无反馈模式启动的显示信息

图10-3 Monkey运行信息
如果想发送1 000个随事件,就输入“adb shellmonkey 1000”,发送的随机事件越多,测试越充分,当然这也要根据待测试软件的状态和测试周期确定。随机事件越多,测试周期越长,测试越充分。
还可以继续对Monkey命令进行改进,让它提供更加贴心的服务。
加上可选项,命令格式如下:

options为Monkey可接受的参数。
10.1.3 Monkey的可选参数及使用
1.常规参数
(1)-h即-help,用来显示Monkey的帮助信息。
可以使用-h了解Monkey所支持的命令及这些命令的用途。代码如下:

运行结果如图10-2所示。
(2)-v表示打印出日志信息,Monkey最多支持3个-v,每多一个-v表示增加一个级别的信息。
在工具执行过程中,不论是开发工程师还是测试工程师都非常希望能够看到一些过程数据,比如一些实时的日志。Monkey支持打印日志信息,命令格式如下:

-v:0级信息,提供的信息量最少,只有一些必需的如启动提示、测试完成和最终结果等信息。
-v-v:1级信息,提供比0级更详细的测试信息,如每个发送到Activity的事件等。
-v-v-v:2级信息,提供更详细的测试信息,如测试中被选中或未被选中的Activity等。
Monkey常规参数总结如图10-4所示。

图10-4 Monkey常规参数总结
2.事件类参数
(1)-s:参数后接随机数生成数的seed值。
Monkey测试是随机测试,如果希望重复执行之前的随机操作应该怎么办呢?Monkey提供了-s参数,这个参数的作用是指定伪随机生成数的seed值。
命令格式如下:

这里采用如下示例命令:

运行结果如图10-5所示。

图10-5 随机数参数运行结果
使用-s参数,可以使用随机操作也可以重复执行。
(2)--throttle:在事件之间插入固定的时间(毫秒)延迟,可以使用这个设置减缓Monkey的运行速度,让其更接近真实用户的操作速度,如果不指定这个参数,则事件之间将没有延迟,事件将以最快的速度生成。
经过前面几个命令的操作,应该已经发现这些随机操作的速度非常快,真实用户肯定不可能操作得这么快,如何才能使测试更接近真实用户的操作速度呢?只需要使用--throttle参数,在每个指令之间加上固定的时间间隔就可以了。命令格式如下:

这里采用如下示例命令:

运行结果如图10-6所示。
这个参数属于常用参数,一般设置为300 ms,原因是实际用户操作最快时300 ms左右产生一个动作事件。
(3)--pct-touch:调整触摸事件的比例。
如果希望调整触摸事件的比例,可使用--pct-touch参数。这里的触摸事件是指在屏幕中的一个down-up事件,即在屏幕某处按下并抬起的动作。命令格式如下:

图10-6 固定时间延迟参数运行结果

该参数是常用参数,其设置要适应当前被测应用程序的操作,比如一个应用80%的操作都是触摸,那就可以将此参数设置得较大。
这里采用如下示例命令:

运行结果如图10-7所示。
(4)--pct-motion:调整动作事件的比例。
如果希望调整动作事件的比例,可以使用--pct-motion参数。动作事件由屏幕上某处一个down事件、一系列伪随机的移动事件和一个up事件组成。命令格式如下:

该参数是常用参数,需注意的是移动事件是直线滑动。比如一个应用80%的操作都是动作,那就可以将此参数设置得较大。
这里采用如下示例命令:

运行结果如图10-8所示。
(5)--pct-trackball:调整滚动球事件的比例。滚动球事件由一个或多个随机的移动事件组成,有时会伴随点击事件。命令格式如下:

图10-7 调整触摸事件比例运行结果

图10-8 调整动作事件比例运行结果

这里采用如下示例命令:

运行结果如图10-9所示。

图10-9 调整滚动球事件比例运行结果
该参数为常用参数,现在手机中几乎没有滚动球,但滚动球事件中包含曲线滑动事件,在被测程序需要曲线滑动时可以选用该参数。
(6)--pct-nav:调整基本导航事件的比例。基本导航事件由方向输入设备的上、下、左、右按键所触发的事件组成。该参数为非常用参数。
命令格式如下:

这里采用如下示例命令:

运行结果如图10-10所示。
(7)--pct-majornav:调整主要的导航事件比例。主要导航事件通常会导致UI中的动作事件,比如5-way键盘中的中间键、回退键和菜单键等。该参数为非常用参数。
命令格式如下:


图10-10 调整基本导航事件比例运行结果
这里采用如下示例命令:

运行结果如图10-11所示。

图10-11 调整主要导航事件比例运行结果
(8)--pct-syskeys:调整系统事件比例。系统事件通常由系统保留使用,如Home、Back、Start Call(拨号键)、End Call(挂断键)及Volume Controls(音量键)等。该参数为非常用参数。
命令格式如下:

这里采用如下示例命令:

运行结果如图10-12所示。

图10-12 调整主要系统事件比例运行结果
(9)--pct-appswitch:调整Activity启动比例。在随机的时间间隔中,Monkey将执行一个startActivity()调用,作为最大限度覆盖被测包中全部Activity的一种方法。该参数为非常用参数。
命令格式如下:

这里采用如下示例命令:

运行结果如图10-13所示。

图10-13 调整Activity启动事件比例运行结果
(10)--pct-anyevent:调整其他事件的比例,包含所有其他事件,如按键、其他在设备上不常用的按钮等。该参数为非常用参数。
命令格式如下:

这里采用如下示例命令:

运行结果如图10-14所示。
下面对Monkey的事件类参数进行总结,如图10-15所示。
3.约束类参数
约束类参数可以让随机事件运行在某几个包或类中。无论Monkey如何动作,都只能在限定的包或类中动作,不会影响其他包或类。

图10-14 调整其他事件比例运行结果

图10-15 Monkey事件类参数总结
(1)-p:指定一个或多个包名。如果指定一个或多个包名,Monkey将只允许访问这些包中的Activity。如果应用程序需要访问这些包(如选择联系人)以外的Activity,需要指定这些包名。如果不指定任何包名,Monkey将允许系统启动所有包的Activity。指定多个包名,使用多个-p,一个-p后面接一个包名。该参数为常用参数。
要想将Monkey限制在某个或几个包中,命令格式如下:

或

这里采用如下示例命令:

运行结果如图10-16所示。

图10-16 指定包名测试运行结果
(2)-c:指定一个或多个类别名。如果指定一个或多个类别名,Monkey将只允许系统启动这些指定类别中列出的Activity。如果不指定任何类别名,Monkey将选择下列类别中列出的Activity:Intent:CATEGORY_LAUNCHER和Intent.CATEGORY_MONKEY。指定多个类别名使用多个-c,每个-c后面接一个类别名。该参数为非常用参数。
要想将Monkey限制在某个或是几个类别中,命令格式如下:

下面对Monkey的约束类参数进行总结,如图10-17所示。

图10-17 Monkey约束类参数总结
4.调试类参数
(1)--ignore-crashes:应用程序发生崩溃或异常时Monkey会停止运行。如果设置此项,Monkey将继续发送事件给系统,直到事件计数完成。命令格式如下:

该参数非常重要,因为人们不希望Monkey测试因为一个应用程序的崩溃而直接停止。
(2)--ignore-timeouts:应用程序发生任何超时错误(如“Application Not responding” 对话框)时Monkey将停止运行。如果设置此项,Monkey将继续发送事件给系统,直到事件计数完成。命令格式如下:

该参数为常用参数,如果不设置此参数,Monkey遇到此类超时对话框时将停止运行。
(3)--ignore-security-exception:当应用程序发生权限错误(例如启动一些需要权限的Activity)导致的异常时,Monkey将停止运行。如果设置此项,Monkey将继续发送事件给系统,直到事件计数完成。命令格式如下:

该参数为常用参数,如果不设置此参数,Monkey遇到此类权限错误时将停止运行。
下面对Monkey的调试类参数进行总结,如图10-18所示。

图10-18 Monkey调试类参数总结
10.1.4 Monkey常用参数小结
Monkey的常用参数还有很多,参见Monkey的官网:
http://developer.android.com/tools/help/monkey.html
案例6:
adb shellmonkey-v-v-v-p com.android.contacts-throttle 5000 1000
(1)2级日志,提供更详细的安装信息,如测试中被选中或未被选中的Activity。
(2)仅测试com.android.Contacts这个包。
(3)每个指令之间延迟5 s。
(4)1 000个随机事件。
对于Monkey的初学者而言,了解这些就足够在工作中应用了。至于Monkey的实现原理和底层技术,读者如果有兴趣可以查找资料进行了解。
10.2 UIAutomator工具
UIAutomator是Google公司推出的用于UI自动化测试的工具,也就是普通的手工测试,点击每个控件元素看输出的结果是否符合预期。比如在登录界面分别输入正确和错误的用户名和密码,然后点击“登录”按钮,检查是否能成功登录或者是否有错误提示等。
10.2.1 UIAutomator的特点
(1)UIAutomator通过注入原生事件进行测试,通过模拟用户操作跟手机的用户界面进行交互并且获取屏幕内容,通过平台的辅助API在远程控件树上获取屏幕内容以及执行一些操作。
(2)UIAutomator具有很好的耦合性,它只需要调用UIAutomation里的一个方法将事件发送给操作系统,就可以模拟一个用户在屏幕上的点击事件。
(3)UIAutomator支持跨应用操作,这个相对容易理解,测试工程师可以编写跨越多个应用程序的脚本进行测试。比如,打开设置应用中的WGPS功能,然后打开另外一个依赖GPS功能的应用,如导航应用等进行交互。
(4)UIAutomator支持事件监听,UIAutomator里的OnAccessibilityEventListener的实现类可以将它的实例传递给setOnAccessibilityEventListener()方法。这个监听接口会在每次事件触发的时候接收到一个回调,从而实现对事件的监听。
(5)UIAutomator的扩展性非常好,因为它提供了接口供人们进行二次开发或小工具的开发。
(6)UIAutomator在没有应用源码的情况下,依然可以完成脚本的编写和控件的捕获。
(7)UIAutomatior非常容易上手,因为它不仅能够轻松地获取控件,而且脚本的编写门槛也非常低。
10.2.2 控件捕获辅助工具
UIAutomator要想发挥最大的作用,也需要其他工具的帮忙,这个工具就是UIAutomator Viewer。可以在Android SDK/tools目录下找到它。
双击打开UIAutomator Viewer后,可以通过它来捕获界面上的控件信息,如图10-19所示。
其中右上方是通过UIAutomator Viewer捕获界面的控件树,如图10-20所示。
右下方是通过UIAutomator Viewer捕获界面的单个控件的详情,如图10-21所示。
10.2.3 UIAutomator的主要API
在正式编写UIAutomator脚本之前,先对UIAutomator的最基本和最关键的API进行简要介绍。如果读者有兴趣,可以访问API的官网,里面有对每个API非常翔实的介绍。
1.手势
这里的手势包括拖拽、滑动、多点触控、缩放。
UIAutomator中拖拽的API是UiObject下的dragTo()方法,具体如下:


图10-19 UIAutomator Viewer控件捕获界面

图10-20 通过UIAutomator Viewer捕获界面的控件树

这个方法可以把一个对象拖拽到屏幕的某一个坐标位置,步长就是拖拽的速度。
滑动是通过UiDevice中的滑动(swipe())方法实现的,具体如下:


图10-21 通过UIAutomator Viewer捕获界面的单个控件详情
此外UIAutomator还提供了更好的滑屏方法,这些方法都来自UiObject。
(1)boolean swipeUp(int step):实现往上滑屏的方法,拖拽对象往上滑动。
(2)boolean swipeDown(int step):实现往下滑屏的方法,拖拽对象往下滑动。
(3)boolean swipeLeft(int step):实现往左滑屏的方法,拖拽对象往左滑动。
(4)boolean swipeRight(int step):实现往右滑屏的方法,拖拽对象往右滑动。
这几个滑屏的API不仅能够让滑屏操作变得精准,还可以避免以往拖拽造成的滑屏过度所引发的误操作。
具体的使用方法如下:

UIAutomator不仅支持多点触控,还封装了缩放手势。UIAutomator通过UiObject提供了多点任意手势的API,具体如下:

实现“多点触控手势”,可定义任意手势。
多点触控平时用得不多,但是缩放手势很常用,因此UiObject直接提供了两个方法:
(1)boolean pinchIn(int percent,int steps):
pinchIn()实现双指向内收缩手势操作,类似平时看图片时的缩小操作。其中percent表示滑到控件对角线的百分比位置,然后停止,steps表示时间,每一步是5ms。
(2)boolean pinchOut(int percent,int steps):
pinchOut()实现双指向外扩张手势操作,类似平时看图片时的放大操作。
案例7:实现手势操作。


2.点击、长按和输入
UIAutomator中点击操作的API是UiObject下的click()方法,具体如下:
boolean click()
click()方法实现了点击对象操作。
UIAutomator还提供了更强大的方法,那就是点击并等待新窗口的方法,这个方法可以让人们再也不用使用sleep()这个固定等待的方法了,具体如下:
boolean clickAndWaitForNewWindow(long timeout)
clickAndWaitForNewWindow()方法实现点击对象并等待新窗口出现,其中的参数为需要等待的时间。也可以直接不带参数,如下所示:
boolean clickAndWaitForNewWindow()表示如果新窗口没有出现就一直等待,没有超时时间。
案例8:实现点击操作。

长按在平时的操作里也是经常需要用到的,UIAutomator里直接通过UiObject的长按方法(longClick())实现:
boolean longClick()
longClick()方法实现了对某对象的长按操作。
UIAutomator通过UiObject的文本设置方法(setText())实现:
boolean setText(String text)
setText()方法实现在对象中输入文本。
案例9:实现文本输入操作。

除此之外,UiObject还提供了另外一个方法,可以让人们直接清除编辑框中的文本,而不用多次点击删除键,如下所示:
void clearTextField()
clearTextField()方法实现清除编辑框中的文本。
3.等待
UIAutomator针对等待操作进行了非常详细的划分,方法如下:
boolean waitForExists(long timeout)
waitForExists()方法实现等待对象出现。
该方法也可以实现等待对象消失,在实际操作中也存在这样的情况,等待某个窗口消失后才进行下一步的操作,这时waitUntilGone()方法就非常适用了,方法如下:
boolean waitUntilGone(long timeout)
waitUntilGone()方法实现等待对象消失。
那么如何判断对象是否已经消失呢?UIAutomator提供了判断对象是否存在的方法,如下所示:
boolean exists()
exists()方法检查对象是否存在。
案例10:实现等待注册按钮出现操作。

4.截图
自动化测试中很多步骤的结果需要使用截图进行保存,比如当出现错误时、实际测试结果跟预期结果不一致时,截图这个功能就显得尤为重要,方法如下:
boolean takeScreenshot(File storePath)
takeScreenshot()方法实现截图并将图像存储到指定路径。
下面通过一个案例来加深印象。
案例11:截图并保存到指定路径,最后导出到计算机进行查看。

完成以上代码后,可以通过在计算机的命令行里执行指定命令,将保存的图片复制到计算机的D盘根目录。命令如下:

5.键值
UIAutomator通过UiDevice提供了大量的按键发送API,下面简单介绍一些常用的方法。
(1)boolean pressBack():
pressBack()方法模拟短按BACK键。
(2)boolean pressHome():
pressHome()方法模拟短按HOME键。
(3)boolean pressMenu():
pressMenu()方法模拟短按MENU键。
(4)boolean pressEnter():
pressEnter()方法模拟短按ENTER键。
(5)boolean pressDelete():
pressDelete()方法模拟短按删除键。
(6)boolean pressRecentApps():
pressRecentApps()方法模拟按最近应用程序按键。
(7)boolean pressSearch():
pressSearch()方法模拟短按搜索键。
6.对象属性获取
获取某一个控件对象的属性在自动化脚本的编写中非常重要,UIAutomator也提供了相应的API,方法如下:
(1)String getPackageName():
getPackageName()方法获取对象包名属性的包名。
(2)String getClassName():
getClassName()方法获取对象包名属性的类名。
下面介绍两个非常实用的获取控件对象属性的方法。
(1)String getContentDescription():
getContentDescription()方法获取控件对象的描述属性。
(2)String getText():
getText()方法获取控件对象的文本属性。
下面还是通过一个实例来加深印象。
案例12:获取TextView对象中的文本。

7.控件选择器
在案例12中出现了一个全新的方法UiSelector(),这个方法可以按照一定的条件筛选出界面上符合条件的控件。
找符合条件的控件,可以从文本、描述、包名或类名、控件ID等几个方面入手,通过一个或多个属性精准定位需要的控件,这样就能保证脚本的稳定性和可移植性。
下面介绍可以通过文本或字符串找到对应控件的方法。
(1)textStartsWith(String text):
textStartsWith()方法通过文本(开始字串匹配)方式进行控件筛选。
(2)textContains(String text):
textContains()方法通过文本(字串包含)方式进行控件筛选。
下面介绍通过描述信息找到对应控件的方法。
(1)description(String desc):
description()方法通过描述(字串匹配)方式进行控件筛选。
(2)descriptionStartsWith(String desc):
descriptionStartsWith()方法通过描述(开始字符匹配)方式进行控件筛选。
(3)descriptionContains(String desc):
descriptionContains()方法通过描述(字串包含)方式进行控件筛选。
下面介绍通过类名、包名和控件ID对控件进行匹配的方法。
(1)className(String className):
className()方法通过类名(字串匹配)方式进行控件筛选。
(2)packageName(String name):
packageName()方法通过包名(字串匹配)方式进行控件筛选。
(3)resourceId(String id):
resourceId()方法通过控件ID方式进行控件筛选。
8.控件采集器
UiCollection继承于UiSelector,它与UiSelector需要配合使用,主要用于枚举一个容器的用户界面元的数量,或按照子元素的文本或描述条件等获取该子元素对象。
可以理解成UiSelector属于一次过滤,找到符合条件的一个控件集合,UiCollection属于二次过滤,精确定位目标控件。
下面简单介绍UiCollection提供的几个方法。
(1)int getChildCout(UiSelector childPattern):
getChildCout()方法获取符合条件的子控件数量。
(2)int getChildByDescription(UiSelector childPattern,String text):
getChildByDescription()方法通过描述进行控件定位。
(3)int getChildByText(UiSelector childPattern,String text):
getChildByText()方法通过文本进行控件定位。
9.滚动
测试中经常会遇到需要屏幕滚动的情况,UIAutomator中对于滚动的定义分成两类,一类是非精确滚动,一种是精确滚动。
(1)非精确滚动相当于平时所说的向上滚动、向下滚动、向前滚动和向后滚动。方法如下:
①boolean scrollForward():
scrollForward()方法实现向前滚动,默认步长为55。
②boolean scrollForward(int steps):
scrollForward()方法实现自定义步长向前滚动。
③boolean scrollBackward(int steps):
scrollBackward()方法实现自定义步长向后滚动。
④boolean scrollBackward():
scrollBackward()方法实现向后滚动,默认步长为55。
⑤setAsHorizontalList():
setAsHorizontalList()方法实现将滚动方向设置为横向。
⑥setAsVerticalList():
setAsVerticalList()方法实现将滚动方向设置为纵向。
下面通过一个案例来加深印象。
案例13:在主界面滚动屏幕找到“模拟登录”。


(2)精确滚动是UIAutomator与众不同的地方,方法如下:
①boolean scrollIntoView(UiSelector selector):
scrollIntoView()方法实现滚动到目标元素所在位置,并尽可能使其位于屏幕正中位置。
②boolean scrollIntoView(UiObject obj):
scrollIntoView()方法实现滚动到目标对象所在位置,并尽可能使其位于屏幕正中位置。
③boolean scrollTextIntoView(String text):
scrollTextIntoView()方法实现滚动到文本对象所在位置,并尽可能使其位于屏幕正中位置。
④boolean scrollTextIntoView(String text):
scrollTextIntoView()方法实现滚动到文本对象所在位置,并尽可能使其位于屏幕正中位置。
⑤boolean scrollDescriptionIntoView(String text):
scrollDescriptionIntoView()方法实现滚动到描述所在位置,并尽可能使其位于屏幕正中位置。
下面结合一个案例来加深印象。
案例14:滚动捕获用户名编辑框对象。

10.监听器
只有添加了监听器,才可以知道什么时候事件被触发,UIAutomator提供了UiWatcher针对某一事件进行监听,方法如下:
(1)void registerWatcher(String name,UiWatcher watcher):
registerWatcher()方法实现注册一个监听器。
(2)void removeWatcher():
removeWatcher()方法实现移除之前注册的监听器。
(3)void resetWatcherTriggers():
resetWatcherTriggers()方法实现重置一个监听器。
(4)void runWatchers():
runWatchers()方法实现强制运行所有监听器。
(5)boolean hasAnyWatcherTriggered():
hasAnyWatcherTriggered()方法实现检查是否有监听器被触发过。
(6)boolean hasWatcherTriggered(String watcherName):
hasWatcherTriggered()方法实现检查某个特定的监听器是否被触发过。
案例15:监听器实例。

UIAutomator主要API总结如图10-22所示。
10.2.4 项目创建
创建UIAutomator项目的过程与创建Java项目的过程是一样的。
首先,打开Eclipse,创建一个Java项目,命名为“LoginDemoTest”,如图10-23所示。
其次,项目创建完成后,用鼠标右键单击“LoginDemoTest”项目,选择“Properties”→“Java Build Path”选项,进入“Libraries”选项卡,选择“Add Library”→“JUint”→“JUint3”选项,添加JUint框架。接着单击“Add External JARS...”按钮,并导航到Android SDK目录,选择Platform目录下的“android.jar”和“uiautomator.jar”两个文件,如图10-24所示。
到此,项目创建完成。下面介绍如何编写脚本。

图10-22 UIAutomator主要API总结
10.2.5 脚本编写
在正式编写脚本之前,简单讲解整个测试过程。
首先,打开UIAutomator并进入模拟登录界面,进行控件的捕获,效果如图10-25所示。
其次,在模拟登录界面进行输入,并捕获控件进行提交,如图10-26所示。

图10-23 创建Java项目

图10-24 导入“android.jar”和“uiautomator.jar”两个文件

图10-25 模拟登录界面

图10-26 在模拟登录界面进行输入
最后,在登录成功界面对显示的结果进行捕获,如图10-27所示。

图10-27 登录成功界面
在了解整个测试过程后,正式开始脚本的编写。
1.启动应用程序
要对应用程序进行测试,那么启动应用程序,然后才能进行下一步操作。UIAutomator通过如下命令启动应用程序:

在进行纯黑盒测试时,无法知道等待测试的应用程序名,应该如何找到这个关键信息呢?
下面介绍一个快速找到应用程序名的方法。在Android开发环境中打开Logcat,清空里面的日志,然后在手机里打开“模拟登录”应用,此时回到LogCat,可以看到图10-28所示界面。
在日志中可以找到:
act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER]...cmp=com.derek.logindemo/.Login
这里“cmp”后面就是应用程序名,如图10-29所示。
现在完善UIAutomator的启用应用的命令如下:


图10-28 Logcat信息

图10-29 应用程序名
在代码里创建一个startApplication()方法,如下:

2.捕获控件
启动应用程序后,需要进行登录操作,因此需要捕获以下控件:
(1)用户名编辑框;
(2)密码编辑框;
(3)登录按钮。
用户名编辑框如图10-30所示。
用户名编辑框节点如图10-31所示。

图10-30 用户名编辑框

图10-31 用户名编辑框节点
用户名编辑框详细参数如图10-32所示。

图10-32 用户名编辑框详细参数
为了更加稳定地获取用户名编辑框,通过文本方式定位它,代码如下:

密码编辑框如图10-33所示。
密码编辑框节点如图10-34所示。

图10-33 密码编辑框

图10-34 密码编辑框节点
密码编辑框详细参数如图10-35所示。

图10-35 密码编辑框详细参数
在详细参数里,发现密码编辑框没有文本,也没有描述(content-desc),那么只能通过ID定位它,代码如下:

登录按钮如图10-36所示。
登录按钮节点如图10-37所示。

图10-36 登录按钮

图10-37 登录按钮节点
登录按钮详细参数如图10-38所示。
在详细参数里,发现登录按钮有文本,那就通过文本来定位它,代码如下:

点击登录按钮后,需要捕获登录成功界面中的用户名,并且判断是否为登录时输入的用户名,如果一致则测试通过,不一致则测试失败。登录成功界面如图10-39所示。
登录成功界面节点如图10-40所示。
登录成功界面详细参数如图10-41所示。

图10-38 登录按钮详细参数

图10-39 登录成功界面

图10-40 登录成功界面节点

图10-41 登录成功界面详细参数
这里不可以使用文本捕获控件,因为这里的文本是需要验证的内容,如果直接通过文本捕获控件,一旦验证结果不是想要的文本,程序就会报错。
正确的做法是在当前页面找到这个文本控件,并把这个文本控件里的文本捕获出来,然后与在登录界面输入的文本进行比较,如果一致就说明显示的用户名是正确的。在图10-40中看到,可以尝试使用resource-id来定位这个文本控件,代码如下:

3.操作控件
操作控件的顺序如下:
(1)点击用户名编辑框,输入用户名;
(2)点击密码编辑框,输入密码;
(3)点击登录按钮;
(4)验证登录成功界面用户名。
操作用户名编辑框的代码如下:

密码编辑框的操作与用户名编辑框类似,这里不再重复。下面点击登录按钮,需要注意的是点击登录按钮后页面会跳转,因此需要调用clickAndWaitForNewWindow()方法等待跳转完成,代码如下:


最后进行登录成功用户名的文本验证,需要通过assertEquals()方法进行判断,代码如下:

4.完整代码
“LoginDemoTest”项目的完整代码如下:




10.2.6 编译运行
根据官方文档的说明,UIAutomator需要通过一系列的命令进行编译,具体如下。
(1)通过下面的命令创建编译“build.xml”文件:

<name>是UIAutomator测试工程的名称,这里是“LoginDemoTest”,而<path>则为项目的完整路径。
针对这个项目,命令如下:

创建完成后,在Eclipse中刷新项目,可以看到了多了一个“build.xml”文件,如图10-42所示。

图10-42 测试项目“LoginDemoTest”

R10-2 Ant下载
(2)接下来的命令需要用到Ant,因此需要下载Ant到本地,然后解压,并在环境变量中配置ANT_HOME。
(3)进入到编译文件“build.xml”所在的路径,并且通过以下命令编译项目:

(4)此时会在“build.xml”文件所在的目录下看到一个bin文件夹,单击进入后可以看到有一个“LoginDemoTest.jar”文件,如图10-43所示。

图10-43 编译生成“LoginDemoTest.jar”文件
(5)将生成的jar包推送到手机指定目录,代码如下:

针对项目采用如下命令:

(6)在手机端执行自动化测试脚本中的测试用例,命令如下:

针对本项目,采用如下命令:

其中<test_class_name>为具体的测试类名,“LoginDemoTest”项目执行完成的结果如图10-44所示。
如果将最后的判断人为地进行修改,让其判断失败,运行结果如图10-45所示。
(7)运行结束后,还有一张截图,现在使用如下命令将图片导入计算机中进行查看:

图10-44 运行成功

图10-45 运行失败

打开截图,效果如图10-46所示。

图10-46 截图效果
10.2.7 工具总结
使用UIAutomator能够大大地降低测试的门槛,而且开发自动化测试脚本的效率也得到了极大的提升,而且在不需要源码的前提下就可以开发出高质量的自动化脚本。
10.3 iTest自动化测试工具
10.3.1 工具介绍
目前市面上有很多针对Android平台的测试工具和测试框架,但缺少一种类似QTP可以录制测试场景,并自动生成测试脚本,然后回放的测试工具。iTest正是基于这样的愿景开发的:iTest可以录制Android智能设备的测试场景并形成脚本,脚本用户可以自己编辑,甚至完全手动编写脚本。脚本是基于控件属性驱动的,这样的脚本复用率高,维护成本低。
10.3.2 工具架构
iTest分为两个部分:PC端脚本录制回放和管理软件、Android手机端agent程序。
Android手机端agent程序的主要功能如下:
(1)负责获取当前Android设备的界面信息,并将这些信息通过socket的方式发送给PC端;
(2)负责接收PC端发送过来的指令信息,并对指令做出相应的处理。例如,接收到点击命令,进行点击操作。
PC端的主要功能如下:
(1)保持与Android手机端agent程序进行通信;
(2)解析UI信息;
(3)实时生成相应的脚本;
(4)实现自动化脚本的解析算法;
(5)录制;
(6)回放。
10.3.3 工具使用
iTest运行在PC上。PC上需要安装:
(1)Net Framework 4.0;
(2)Android SDK(推荐安装);
(3)Android手机的驱动程序。
双击iTest安装文件“setup.exe”,选择安装路径后完成安装。
安装完成后,iTest会在桌面和“开始”菜单中生成快捷方式,如图10-47所示。

图10-47 iTest快捷方式图标
双击快捷方式启动iTest,主界面分为两个部分:脚本录制界面和脚本管理界面。
1.脚本录制界面
脚本录制界面主要用于连接测试设备,测试场景,调试和生成测试脚本,最后保存脚本文件,如图10-48所示。
1处是已经连接的设备名称。如果打开iTest之前设备已经连接在PC上,iTest启动之后会自动连接设备并显示名称。
2处是Android手机的界面窗口,会将Android手机的当前界面映射到该处,录制时可以直接操作此处。界面会实时刷新。
3处从左到右分别是录制开始按钮、debug执行按钮(此按钮将脚本文件保存后才可以使用)、停止录制按钮。
4处是脚本内容自动生成区域。
5处是debug调试信息、日志的输出窗口。
6从左到右分别对应Android手机上的BACK、HOME、MENU键。
7处是脚本录制界面和脚本管理界面的切换按钮。
8处是保存脚本的按钮。

图10-48 脚本录制界面
2.脚本管理界面
脚本管理界面主要用于新建测试计划或者导入已经存在的测试计划并执行如图10-49所示。

图10-49 脚本管理界面
1处从左到右分别是新建测试计划按钮、导入测试计划按钮。
2处是新建/导入的测试计划显示窗口,以树形结构显示。
3处是脚本内容显示窗口。
4处是case执行过程中日志输出窗口。
5处是暂停、停止按钮。
单击开始录制按钮后,iTest会显示正在准备测试环境的提示框,如图10-50所示。
连接成功后,Android手机的屏幕会映射到iTest窗口上,并提示用户在手机端打开Accessibilitor,如图10-51所示。

图10-50 正在准备测试环境的提示框

图10-51 提示用户在手机端打开Accessibilitor
在手机上选择“Settings”→“Accessibility”选项,打开UIAccessibilitor,如图10-52所示。

图10-52 打开UIAccessibilitor
此时iTest如图10-53所示。
单击iTest下方 的Back键按钮,会自动生成测试脚本:

同时,屏幕会自动刷新,更新到当前界面,如图10-54所示。

图10-53 iTest界面(1)

图10-54 iTest界面(2)
点击iTest下方 的Home键按钮,会自动生成测试脚本:

同时,屏幕会自动刷新,更新到当前界面,当前界面为Home界面,如图10-55所示。
点击iTest下方 的Menu键按钮,会自动生成测试脚本:

图10-55 Home界面

同时,屏幕会自动刷新,更新到当前界面,当前界面显示出设置界面,如图10-55所示。
例如鼠标左键单击图10-56中的Menu菜单,iTest会弹出一个选择菜单,供用户选择点击的方式,有以下4种类型(图10-57):
(1)坐标:通过坐标点击;
(2)ID:通过控件的ResourceId点击;
(3)Text:通过控件的Text点击;
(4)滑屏:录制滑动屏幕事件。

图10-56 设置界面
坐标点击生成如下脚本:

其中95,432是位置坐标。
ID生成如下脚本:

其中android:id/title是鼠标点击的控件的resource ID。
Text生成如下脚本:

表示点击了Wallpaper这个item。
滑屏生成如下脚本:


图10-57 点击方式生成的脚本
两个坐标分别是对应起始点和终点的位置坐标。
每次录制的动作之间,iTest会自动插入Sleep()语句,用户可以自己调整Sleep的时间,时间单位为毫秒。
在屏幕上单击鼠标右键,iTest会弹出选择框,用户可以选择“ID比对”“Text比对” 两种类型。
选择”ID比对”类型后,会生成如下脚本:

其中,Check_ID_Exist是iTest为测试脚本开发人员提供的一个测试判断方法,该方法负责判断resource ID是否存在,若存在则返回true,若不存在则返回false,两个参数分别是resource ID和超时时间。
WaitForSnapShot()是iTest提供的一个方法,该方法负责截取当前手机屏幕,并将图片文件以当前脚本名称和时间点命名,保存到iTest安装目录下的Errorsnaps目录下。
选择”Text比对”类型后,会生成如下脚本:

Check_Text_Exist()方法的第一个参数为文本,第二个参数为超时时间。
录制完成后,单击停止录制按钮,然后单击保存按钮,iTest会弹出“另存为”对话框,用户可以自定义保存路径和保存文件名,如图10-58所示。

图10-58 “另存为”对话框
测试人员也可以自己在脚本编辑窗口中手动编写测试脚本,编写完成后保存。编写脚本所需的测试方法将在最后以Excel格式的文件附上“回放”按钮显示,可以试运行脚本,单击“回放”按钮后,iTest提示设置执行次数,如图10-59所示。

图10-59 “设置测试执行次数”对话框
设置完执行次数后单击“确认”按钮。iTest将开始执行刚录制的脚本内容,同时会显示脚本执行时间、当前的执行次数,debug窗口会输出脚本执行过程中的日志信息,如图10-60所示。

图10-60 iTest执行刚录制的脚本内容
所谓测试计划,即一个测试用例的集合。对一个功能或者场景进行测试时,可能需要几条测试用例才能完成,这时可以用一个测试计划将相关的测试用例集合在一起。测试计划可以重复使用。
在脚本管理界面,单击“New”按钮,将创建一个没有测试用例的测试计划,“新建测试计划”对话框如图10-61所示。
输入测试计划的名称后,单击“确认”按钮,测试计划界面如图10-62所示。
此时没有测试用例,需要根据测试场景导入不同的测试用例。选中测试计划,单击鼠标右键,选择“增加case”命令,弹出“打开”对话框,如图10-63所示。

图10-61 “新建测试计划”对话框

图10-62 测试计划界面

图10-63 “打开”对话框
选择需要导入的测试用例,单击“打开”按钮即可导入。
双击某个测试用例,该测试用例的脚本内容将会在右边的脚本窗口中显示出来,用户可以随时修改,并通过“Ctrl+S”组合键更新保存,如图10-64所示。
选择某条要删除的用例,单击鼠标右键选择“删除”命令即可。
需要对某条测试用例的脚本内容进行修改时,可以使用更新用例的功能。
双击该条测试用例,脚本编辑窗口将显示脚本内容,用户可以对脚本内容进行修改,修改后按“Ctrl+S”组合键,脚本内容将更新为当前内容并保存。
单击右侧的执行测试用例按钮,开始执行该测试用例合集。iTest会实时显示各个测试用例的状态,如图10-65所示。

图10-64 测试用例的脚本内容

图10-65 执行测试用例合集
绿色圆圈表示运行中,绿色的”√”表示成功,红色的“×”表示失败,同时也会显示当前执行次数和执行的日志信息。
在测试用例执行过程中,如果执行失败,测试人员需要收集测试设备的日志信息,以供开发人员分析。
iTest在开始执行测试用例时,会自动收集测试日志,并以“测试用例名+测试时间” 的文件名保存在安装目录下的Logs文件夹下。测试用例执行失败时,脚本中的截屏图片会保存在安装目录下Logs文件夹下的ErrorSnaps文件夹下,如图10-66所示。

图10-66 ErrorSnaps文件夹
iTest给测试人员提供了各种测试API,以方便编写脚本。脚本开发人员可以直接使用这些API。
10.4 小结
到目前为止本节已经将市面上比较流行的Android自动化测试工具介绍完毕——从自动化测试的准备工作,到随机的Monkey测试,从Google提供的用于UI自动化测试的工具UIAutomator到可以录制测试场景并自动生成测试脚本,然后回放的测试工具iTest。
如果读者想成为一名合格的Android应用开发者和软件测试工作者,还需要更多的实践项目来不断地提升自己。