一、简介
android sdk api 16开始,Android SDK开始支持两个做功能UI测试的新工具。
(1)uiautomatorviewer,用以扫描以及分析Android应用程序的UI部件的工具。
(2)uiautomator ,提供API用以自定义UI测试的Java库。
要应用上面两个工具,除了需要android sdkapi 16以上的前提条件外,还要求Android SDK Tools为21版以上。
优点:1.可以对所有操作进行自动化,操作简单;
2.不需要对被测程序进行重签名,且,可以测试所有设备上的程序,比如~某APP,比如~拨号,比如~发信息等等
3.对于控件定位,要比robotium简单一点点
缺点:1.uiautomator需要android level 16以上才可以使用,因为在level 16及以上的API里面才带有uiautomator工具
2.如果想要使用resource-id定位控件,则需要level 18及以上才可以
3.对中文支持不好(不代表不支持,第三方jar可以实现)
二、常用API
UiAutomator主要涉及一下几个类,大多数位于源码包的com.android.uiautomator.core下,以下几个类是平时在写脚本中用的最多的:UiDevice UiSelector UiScrollable UiObjec UiCollection
每个测试用例都需要继承UiAutomatorTestCase,以实现测试环境的setup,teardown等。而UiAutomatorTestCase则是通过继承Junit3中的TestCase类,并在其中的setUp() 、tearDown() 、getParams()函数中。其中主要是用Bundle实现Android Activity之间的通讯。在UiAutomatorTestCase,还加入了getUiDevice()等关于UiDevice的函数,以实现在测试的任意地方均可调用UiDevice()。
(一)UiDevice:用与访问有关设备状态的信息,也可以使用这个类来模拟用户在设备上的操作。
可以通过下面的方法得到实例: UiDevice mdevice= getUiDevice();
l 获取坐标参数
函数返回值 | 函数体 | 说明 | 实例 |
boolean | click(int x, int y) | 模拟用户在指定位置点击 | mdevice.click(200, 300) 点击屏幕的200,300坐标处 |
int | getDisplayHeight() | 获得当前设备的屏幕分辨率的高 | 例如,我的手机1920*1080,得到的是 1920 |
int | getDisplayWighth() | 获得当前设备的屏幕分辨率的宽 | 例如,我的手机1920*1080,得到的是 1080 |
point | getDisplaySizeDp() | 获取显示尺寸大小 |
|
l 系统信息
函数返回值 | 函数体 | 说明 | 实例 |
String | getCurrentActivityName() | 获得的是应用程序在桌面上显示的名字 | 例如,在qq首页得到的是“QQ”,在微信登录页得到的是“微信”,注意,这个得到的不是Activity的名字 |
String | getCurrentPackageName() | 获得当前显示的应用程序的包名 | 例如,在微信启动的时候,获得的是“com.tencent.mm” |
l 滑动、拖拽
函数返回值 | 函数体 | 说明 | 实例 |
boolean | swipe(int startX, int startY, int endX, int endY, int steps) | 用指定的步长,从A点滑动B点 | 例如,需要从(10, 10)点用两步滑动到(100, 200)点,则需要mdevice.swipe(10, 10, 100, 200, 2) |
boolean | drag(startX, startY, endX, endY, steps) | 拖拽坐标处对象到另一个坐标 |
|
boolean | swipe(segments, segmentSteps) | 在Points[]中以segmentSteps滑动 |
|
l 系统按键
函数返回值 | 函数体 | 说明 | 实例 |
boolean | isScreenOn() | 判断手机当前是否灭屏 | 当手机灭屏的时候,得到是“false”,手机亮屏,得到的是“true” |
void | wakeUp() | 点亮当前屏幕 | 调用后,相当于按下了电源键,如果手机设置了滑动锁屏,滑动锁屏还是在的,不会自动解开 |
boolean | pressBack() | 点击back键 |
|
boolean | pressHome() | 点击home键 |
|
boolean | pressMenu() | 点击menu键 |
|
boolean | pressKeyCode(int keyCode) | 利用keycode值模拟一次按下事件 | 例如,需要按下数字1 数字1的keycode是 KEYCODE_NUMPAD_1,更多keycode可以在 http://developer.android.com/intl/zh-cn/reference/android/view/KeyEvent.html 进行查询 |
void | unfreezeRotation() | 启用传感器,并允许旋转 |
|
boolean | takeScreenshot(File storePath) | 截取当前屏幕,保存到文件 | 例如,File files = new File("/sdcard/res.jpg"); mdevice.takeScreenshot(files); 即可将截图保存到sd卡中了。 |
l 等待窗口
函数返回值 | 函数体 | 说明 |
void | waitForIdle() | 等待当前窗口处于空闲状态、默认10s |
void | waitForIdle(long timeout) | 自定义超时等待当前窗口处于空闲状态 |
void | waitForWindowUpdate(packageName, timeout) | 等待窗口内容更新 |
(二)UiSelector:按照一定的条件(例如控件的text值,资源id),定位界面上的元素。如果一个条件无法定位,那么可以通过多个条件组合,来定位一个元素。如果发现多个满足条件的控件则会返回第一个控件。在构造UiSelector的时候可以 组合使用多个属性来定位具体的控件。如果没有找到控件则会抛出 UiAutomatorObjectNotFoundException 异常。UiSelector对象的最终目的是去构造一个UiObject对象。
l 根据text构造
函数返回值 | 函数体 | 说明 | 实例 |
UiSelector | text(String text) | 根据“控件text属性的内容”构造出UiSelector对象 | 一个控件text的值是“发现”,UiSelector s = new UiSelector().text("发现"); |
UiSelector | textContains(String text) | 根据“控件text属性包含的内容”构造出UiSelector对象 | 同上例子:UiSelector s = new UiSelector().textContains("现"); |
UiSelector | textMatches(String regex) | 根据“控件text属性正则表达式的内容”构造出UiSelector对象 | 正则表达式语法参考网上资料即可。 |
UiSelector | textStartsWith(String text) | 根据“控件text属性开始的内容”构造出UiSelector对象 | 同上例子:UiSelector s = new UiSelector().textStartsWith("发"); |
比较常用,准确度也比较高,中文查找的时候,如果遇到 “UiOjbectNotFoundException”的时候,记得把项目的编码格式改为utf-8。
l 根据description构造
函数返回值 | 函数体 | 说明 | 实例 |
UiSelector | description(String desc) | 根据“控件content-desc属性的内容”构造出UiSelector对象 | 一个控件text的值是“发现”,UiSelector s = new UiSelector().text"发现("); |
UiSelector | descriptionContains(String desc) | 包含** | 同上例子:UiSelector s = new UiSelector().textContains("现"); |
UiSelector | descriptionMatches(String regex) | 正则 | 正则表达式语法参考网上资料即可。 |
UiSelector | descriptionStartsWith(String desc) | 以**开始 | 同上例子:UiSelector s = new UiSelector().textStartsWith("发"); |
l 根据资源id
函数返回值 | 函数体 | 说明 |
iSelector | resourceId(String id) | 根据资源id获取对象,例如:UiSelector s = new UiSelector().resourceId("com.tencent.mm:id/b8m") |
UiSelector | resourceIdMatches(String regex) | 根据资源id的正则表达式获取对象 |
如果父类下面的子类有相同的resource-id,则可以通过instance(?)来判定,比较常用
• 根据类class
(1)UiSelector className(String className):根据控件的类名来找到UiSelector对象。
(2)UiSelector s = newUiSelector().className("android.widget.TextView").instance(1);获得相同类名下的第2个对象index(index)与instance(instance)类似
(3)UiSelector childSelector(UiSelector selector):有的时候子控件不好获得,而其父控件比较好获得的时候:
先得到父控件:UiSelectors_p = new UiSelector().resourceId("com.tencent.mm:id/axj");
其次 UiSelectors_c= s_p.childSelector( newUiSelector().className("android.widget.EditText") );
在它的父控件的childSelector方法中传入一个带有一定特征的UiSelector对象,即可得到子控件。
(4)UiSelector fromParent(UiSelector selector):父控件不好获得,而是同级的控件(同属一个parent)比较好获取
先得到同级的UiSelector对象:UiSelector s1 = new UiSelector().resourceId("com.tencent.mm:id/axc");
再得到和它同样一个父控件的ImageView的UiSelector对象:UiSelector s2 = fromParent( newUiSelector().className("android.widget.ImageView") );
(三)UiObject:UiObject是UiAutomator的核心属性之一。它代表了整个UI界面中的所有对象元素。 它的功能包括:获取UI元素,点击、拖拽、滑动、对象属性判断、手势等。通过UiSelector来查找UiObject。
函数返回值 | 函数体 | 说明 |
boolean | click() | 点击对象 |
boolean | clickAndWaitForNewWindow() | 点击对象并等待新窗口出现 |
boolean | clickAndWaitForNewWindow(timeout) | 点击对象并等待新窗口出现,指定延迟 |
boolean | longClick() | 长按对象 |
boolean | dragTo(destX, destY, steps) | 以steps拖动对象到坐标 |
boolean | swipeDown(steps) | 向下拖动 |
boolean | setText(text) | 设置内容为text |
boolean | clearTextField() | 清除文本 |
boolean | isCheckable() | 获取对象checkable状态 |
boolean | waitForExists(timeout) | 等待对象出现 |
boolean | exists() | 对象是否存在 |
(四)UiScrollable:代表可滚动的控件。可以用UiScrollable来模拟水平或者垂直滚动的UI元素。如果需要操作的元素在屏幕外需要滚动屏幕才能看到的情况下需要使用UiScrollable
(五)UICollection:通常用于获取满足某种搜索条件的组件集合,通过链式搜索确定最终需要的组件。 先按照一定的条件枚举容器内的子元素,再从符合条件的子元素中进一步定位。 一般使用容器类组件作为父类,用于寻找不好定位的子元素。代表控件的集合。获取UiCollection的方式和UiObject一样,通过 UiSelector查找。 UiCollection对应Android系统中的ViewGroup以及子控件。
(六)UIWatcher:通常我们会让脚本来按照我们所需要的顺序来执行,但有时候短信、电话来了,打断了脚本的执行。 所以,我们的脚本必须要有一定的容错性。UiWatcher正是这样一个容错的对象,当我们在顺序执行脚本时,如果中间突然插入了一些不明事件,我们可以使用UiWatcher来拦截异常,处理完异常后,再返回原来的脚本执行顺序。经常用来处理一些意外事件,如弹窗等。
(七)Configuration:对默认操作的配置,通常情况下,我们使用默认的Configuration就足够了,当然,如果你有一些特殊需求,就可以通过Configuration类来设置。它能更改我们前面提到的所有默认属性的设置。包括默认延迟、输入延迟、等待超时等等。
三、查看测试报告
下面是一个典型的UiAutomator测试报告:
INSTRUMENTATION_STATUS:numtests=1
INSTRUMENTATION_STATUS: stream=
com.hj.autotest.AutoTest:
INSTRUMENTATION_STATUS: id=UiAutomatorTestRunner
INSTRUMENTATION_STATUS:test=testDevice
INSTRUMENTATION_STATUS:class="com".hj.autotest.AutoTest
INSTRUMENTATION_STATUS:current=1
INSTRUMENTATION_STATUS_CODE: 1
INSTRUMENTATION_STATUS:numtests=1
INSTRUMENTATION_STATUS:stream=.
INSTRUMENTATION_STATUS:id=UiAutomatorTestRunner
INSTRUMENTATION_STATUS:test=testDevice
INSTRUMENTATION_STATUS:class="com".hj.autotest.AutoTest
INSTRUMENTATION_STATUS:current=1
INSTRUMENTATION_STATUS_CODE: 0
INSTRUMENTATION_STATUS: stream=
Test results forWatcherResultPrinter=.
Time: 31.489
OK (1 test)
INSTRUMENTATION_STATUS_CODE: -1
这些报告被INSTRUMENTATION_STATUS_CODE分为了三个部分,1表示运行前,-1表示运行完成。如果出错了,可以在报告中找到相应的错误信息。同样需要知道的是,UiAutomator也是JUnit工程,同样可以在里面使用断言来进行某些变量、结果值的测试,这些同样会在报告中体现出来。
current:当前运行的测试顺序编号,故和方法名有关
class:当前运行方法所在类的类名
numtests:测试总数,一个publictestXXX方法就是一个测试
test:当前测试的方法名
INSTRUMENTATION_STATUS_CODE:测试状态码,一般1是正在测试,0是测试通过,-1是错误