11.1.4 UiAutomator API详解
介绍完UiAutomator如何配置,接下来介绍UiAutomator的API。图11-43是UiAutomator的类的调用图。
图11-43 UiAutomator的类图
1.UiDevice类介绍
(1)获取UiDevice实例的方式[J1]
- UiDevice.getInstance();[J2]
- getUiDevice()。
注:第二种方式为获取UiDevice实例,当含有该实例的类被别的类调用时,会报空指针错误。所以一般仅用第一种方式。
案例11-1:获取UiDevice实例的两种方式。
package com.uiautomatortest; import android.os.Bundle; import android.os.RemoteException; import com.android.uiautomator.core.UiDevice; import com.android.uiautomator.testrunner.UiAutomatorTestCase; public class TestGetUiDevice2 extends UiAutomatorTestCase { public void testDevice(){ TestGetUiDevice device1=new GetUiDevice(); device1.press(); } public static void main(String[] args){ String jarName,testClass,testName,androidId; jarName="DemoTest"; testClass="com.uiautomatortest.Test"; testName="testDevice"; androidId="1"; new UiAutomatorHelper(jarName,testClass,testName,androidId); } }
类TestGetUiDevice2里调用类TestGetUiDevice1中包含getUiDevice()的press方法时报空指针错误:java.lang.NullPointerException。
如果将TestGetUiDevice1.java中的getUiDevice()换成UiDevice.getInstance(),则可以正常运行。修改后的TestGetUiDevice1.java如下:
package com.uiautomatortest; import com.android.uiautomator.core.UiDevice; import com.android.uiautomator.testrunner.UiAutomatorTestCase; public class TestGetUiDevice1 extends UiAutomatorTestCase { public void press(){ // getUiDevice().pressMenu(); // getUiDevice().pressHome(); UiDevice.getInstance().pressMenu(); UiDevice.getInstance().pressHome(); } }
所以,都是采用UiDevice.getInstance()方式获取UiDevice实例。
(2)UiDevice功能[J3]
获取设备信息:屏幕分辨率、旋转状态、亮灭屏幕状态等。
操作:按键、坐标操作、滑动、拖拽、灭屏唤醒屏幕、截图等。
监听功能。
手机常见按键如下:
HOME:主屏幕键。
MENU:菜单键。
BACK:返回键。
VOLUME_UP:音量加键。
VOLUME_DOWN:音量减键。
RecentApps:最近使用APP。
POWER:电源键。
Dpad:上下左右。
UiDevice按键API说明见表11-1。
表11-1 UiDevice按键API说明
返回值 |
方法名 |
描述 |
boolean |
pressBack() |
模拟短按返回back键 |
boolean |
pressDPadCenter() |
模拟轨迹球中点按键 |
boolean |
pressDPadDown() |
模拟轨迹球向下按键 |
boolean |
pressDPadLeft() |
模拟轨迹球向左按键 |
boolean |
pressDPadRight() |
模拟轨迹球向右按键 |
Boolean |
pressDPadUp() |
模拟轨迹球向上按键 |
boolean |
pressDelete() |
模拟按删除delete键 |
boolean |
pressEnter() |
模拟按回车键 |
boolean |
pressHome() |
模拟按home键 |
boolean |
pressKeyCode(int keyCode,int metaState) |
模拟按键盘代码keyCode |
boolean |
pressKeyCode(int keyCode) |
模拟按键盘代码keyCode |
boolean |
pressMenu() |
模拟按menu键 |
boolean |
pressRecentApps() |
模拟按最近使用程序 |
boolean |
pressSearch() |
模拟按搜索键 |
案例11-2:UiDevice按键API。
package com.uiautomatortest; import android.os.Bundle; import android.os.RemoteException; import com.android.uiautomator.core.UiDevice; import com.android.uiautomator.testrunner.UiAutomatorTestCase; public class Test extends UiAutomatorTestCase { public void testHome (){ UiDevice.getInstance().pressHome(); Sleep(2000); } public void testMenu(){ UiDevice.getInstance().pressMenu(); Sleep(2000); } public void testRecent() throws RemoteException{ UiDevice.getInstance().pressRecentApps(); Sleep(2000); } }
(3)KEYCODE键盘映射码,包括:
KeyEvent:按键事件。
META KEY。
辅助功能键:ALT、SHIFT、CAPS_LOCK。
KEYCODE键盘映射码见表11-2。
表11-2 KEYCODE键盘映射码
列 |
激活状态 |
metaState |
base |
META_key未被激活 |
0 |
caps |
Shift或Caps Lock被激活 |
1 |
fn |
Alt被激活 |
2 |
caps_fn |
Alt、Shift或Caps Lock同时被激活 |
3 |
案例11-3:KEYCODE键盘映射码。
public void testKeyCode (){ UiDevice.getInstance ().pressKeyCode(KeyEvent.KEYCODE_A); //小写a UiDevice.getInstance ().pressKeyCode(KeyEvent.KEYCODE_B); //小写b UiDevice.getInstance ().pressKeyCode(KeyEvent.KEYCODE_C); //小写c UiDevice.getInstance ().pressKeyCode(KeyEvent.KEYCODE_A,1); //大写A UiDevice.getInstance ().pressKeyCode(KeyEvent.KEYCODE_B,1); //大写B UiDevice.getInstance ().pressKeyCode(KeyEvent.KEYCODE_C,1); //大写C }
(4)在Eclipse中如何查看一个Android模拟器的内部文件[J4]
需要进行UiAutomator测试,往往需要知道系统中含有哪些应用程序包以及包的名称,可以在Eclipse中添加DDMS工具来查看,如图11-44所示。
2.坐标相关的知识
手机屏幕坐标:左上角开始到右下角结束。
DP:设备独立像素,如320像素显示到640像素上要拉伸一倍。
Point:代表一个点(x,y),左上角的坐标永远为(0,0)。
(1)坐标相关API见表11-3。
图11-44 在Eclipse中添加DDMS工具
表11-3 坐标相关API
返回值 |
方法名 |
描述 |
boolean |
Click(int x,int y) |
使用坐标点击屏幕 |
int |
getDisplayHeight() |
获取屏幕高度 |
Point |
getDisplaySizeDP() |
获取显示尺寸,返回显示大小(设备独立像素) 屏幕旋转返回的显示大小调整 |
int |
getDisplayWidth() |
获取屏幕宽度 |
(2)使用UiAutomator Viewer获取屏幕快照。
进入android SDK的tools目录下找到uiautomatorviewer.bat,双击打开这个工具,就可以使用了,如图11-45所示。
图11-45 UiAutomator Viewer
案例11-4:关于坐标的API。
package com.uiautomatortest; import android.graphics.Point; import android.os.Bundle; import android.os.RemoteException; import android.view.KeyEvent; import com.android.uiautomator.core.UiDevice; import com.android.uiautomator.testrunner.UiAutomatorTestCase; public class Test extends UiAutomatorTestCase { public void testClick(){ //get the display height and width int h=UiDevice.getInstance().getDisplayHeight(); int w=UiDevice.getInstance().getDisplayWidth(); Point p=UiDevice.getInstance().getDisplaySizeDp(); System.out.println("The display width is: "+w); System.out.println("The display height is: "+h); System.out.println(p); //click the clock UiDevice.getInstance().click(159,223); } }
3.拖曳与滑动
(1)概念介绍
- 拖曳:将组件从一个坐标移动到另一个坐标。
- 移动:从一个坐标点移动到另一个坐标点。
- 步长:从一点滑动到另一点使用的时间。
(2)拖曳与滑动的相关API(表11-4)
表11-4 拖曳与滑动的相关API
返回值 |
方法名 |
描述 |
boolean |
drag(int startX,int startY,int endX,int endY,int steps) |
把对象从一个坐标拖动到另一个坐标 |
boolean |
swipe(Point[] segment,int segmentSteps) |
在点阵列中滑动,5ms一步 |
boolean |
swipe(int startX,int startY,int endX, int endY, int steps) |
通过坐标滑动屏幕 |
案例11-5:关于拖曳与滑动的API。
package com.uiautomatortest; import android.graphics.Point; import android.os.Bundle; import android.os.RemoteException; import android.view.KeyEvent; import com.android.uiautomator.core.UiDevice; import com.android.uiautomator.testrunner.UiAutomatorTestCase; public class Test extends UiAutomatorTestCase { public void testDragAndSwipe(){ //[64,577][128,640] int startX,startY,endX,endY,steps; startX=(128-64)/2+64; startY=(640-577)/2+577; endX=startX; endY=startY-200; steps=100; UiDevice.getInstance().drag(startX,startY,endX,endY,steps); Point p1=new Point(); Point p2=new Point(); Point p3=new Point(); Point p4=new Point(); p1.x=78;p1.y=30; p2.x=235;p2.y=309; p3.x=224;p3.y=414; p4.x=76;p4.y=409; Point[] ps={p1,p2,p3,p4,p1}; UiDevice.getInstance().swipe(ps,50); //(278,374),(69,373) int startX=278; int startY=374; int endX=69; int endY=373; int steps=100; UiDevice.getInstance().swipe(startX,startY,endX,endY,steps); } }
4.屏幕旋转
(1)屏幕旋转相关知识
旋转方向:0°、90°(向左转)、180°、270°(向右转)。
重力感应器:重力感应器是旋转所依靠的。
固定位置:指将屏幕方向固定在0°、90°或者180°等。
物理旋转:物理旋转与重力感应器关联在一块,关闭物理旋转就是关闭了重力感应器,反之亦然)。
(2)旋转屏幕相关API(表11-5)
表11-5 旋转屏幕相关API
返回值 |
方法名 |
描述 |
void |
setOrientationLeft() |
通过禁用传感器,然后模拟设备向左转,并且固定位置 |
void |
setOrientationNatural() |
通过禁用传感器,然后模拟设备转到其自然默认的方向,并且固定位置 |
void |
setOrientationRight() |
通过禁用传感器,然后模拟设备向右转,并且固定位置 |
void |
unfreezeRotation() |
重新启动传感器和允许物理旋转 |
boolean |
isNaturalOrientation() |
检测设备是否处于默认旋转状态 |
int |
getDisplayRotation() |
返回当前的显示旋转,0°、90°、180°、270°值分别为0、1、2、3 |
void |
freezeRotation() |
禁用传感器和冻结装置物理旋转在其当前旋转状态 |
案例11-6:关于屏幕旋转的API。
package com.UiAutomator; import java.io.File; import android.os.Bundle; import android.os.RemoteException; import android.view.KeyEvent; import com.android.uiautomator.core.UiDevice; import com.android.uiautomator.testrunner.UiAutomatorTestCase; public class Test1 extends UiAutomatorTestCase { public void testOrientation() throws RemoteException{ int r=UiDevice.getInstance().getDisplayRotation(); if(r==0){ System.out.println("r="+r); UiDevice.getInstance().setOrientationLeft(); } If(r==1){ UiDevice.getInstance().setOrientationNatural(); Sleep(1000); UiDevice.getInstance().setOrientationLeft(); } If(r==2){ UiDevice.getInstance().setOrientationNatural(); sleep(1000); UiDevice.getInstance().setOrientationLeft(); } If(r==3){ UiDevice.getInstance().setOrientationNatural(); } } } int r=UiDevice.getInstance().getDisplayRotation(); If(r==0){ System.out.println("r="+r); UiDevice.getInstance().setOrientationLeft(); } If(r==1){ UiDevice.getInstance().setOrientationNatural(); Sleep(1000); UiDevice.getInstance().setOrientationLeft(); } If(r==2){ UiDevice.getInstance().setOrientationNatural(); Sleep(1000); UiDevice.getInstance().setOrientationLeft(); } If(r==3){ UiDevice.getInstance().setOrientationNatural(); } } }
5.UiSelector对象
这个对象可以理解为一种条件对象,描述的是一种条件,经常配合UiObject使用,可以得到某个(某些)符合条件的控件对象。
- Checked(boolean val)
描述一种check状态为val的关系。
- className(className)
描述一种类名为className的对象关系。
- clickable(boolean val)
与checked类似,描述clickable状态为val的关系。
- description(desc)
介绍。
- descriptionContains(desc)
与description类似。
- focusable(boolean val)
与checked类似。
- index(index)
当前对象在父对象集中的索引作为描述。
- packageName(Stringname)
用包名作为条件描述。
- selected(val)
描述一种选择关系。
- text(text)
最常用的一种关系,用控件上的文本即可找到当前控件。需要注意,所有使用text属性找到的控件,必须是英文的。也就是说,不支持通过中文查找控件。
- textContains(text)
与text类似。
- textStartsWith(text)
与text类似。
6.UiObject对象
这个对象可以理解为控件的对象。一般地,UiObject对象可以通过以下形式得到:
UiObject mItem = new UiObject(new UiSelector().text("English"));
也就是配合一个UiSelector,就可以得到一个控件,具体见下面的案例分享。
- click()
点击控件。
- clickAndWaitForNewWindow()
点击某个控件,并等待窗口刷新。
- longClick()
长按。
- clearTextField()
清除文本,主要针对编辑框。
- getChildCount()
这个方法可以看出,其实UiObject也可以是一个控件的集合。
- getPackageName()
得到控件的包名。
- getSelector()
得到当前控件的选择条件。
- getText()
得到控件上的Text。
- isCheckable()
- isChecked()
- isClickable()
- isLongClickable()
- isScrollable()
- isScrollable()
- isSelected()
判断是否具备某个属性。
7.UiCollection对象
这个对象可以理解为一个对象的集合。因为UiSelector描述后得到的有可能是多个满足条件的控件集合,因此可以用来生成UiCollection。
UiCollection mUiCollection = new UiCollection(new UiSelector().text("Settings"));
getChild(selector);
从集合中再次通过UiSelector选择一个UiObject对象。
- getChildByDescription(childPattern,text)
从一个匹配模式中再次以text为条件选择UiObject。
getChild(selector) |
从集合中再次通过UiSelector选择一个UiObject对象 |
getChildByDescription(childPattern, text) |
从一个匹配模式中再次以text为条件选择UiObject |
8.API详解
对于功能测试用例,第一步获取对象,第二步进行操作,第三步进行判断。
(1)获取对象
根据笔者经验获取对象最好用的方法是:
UiSelector button=newUiSelector().resourceId("com.example.demo4:id/button1");
obj = new UiObject(button);
Obj.click();
其中com.example.demo4安卓APP的类名,button1为R.java中对应元素的id名。
public static final class id {
public static final intaction_settings=0x7f08000c;
public static final int button1=0x7f080005;
…
另外还可以通过以下方法来定位。
案例11-7:构建一个以‘微’开头的UiSelector。
UiSelector wx=new UiSelector().textStratWith(“微”) ; UiObject obj = new UiObject(wx); Obj.click();
案例11-8:构建一个有‘微’的UiSelector。
UiSelector wx=new UiSelector().textContains(“微”) ; UiObject obj = new UiObject(wx) ; obj.click ();
案例11-9:构建一个class属性为android.widget.TextView,text属性为微信的UiSelector。
UiSelector wx=new UiSelector().className(“android.widget.TextView”).text(“微信”); UiObject obj = new UiObject(wx); obj.click();
案例11-10:构建一个聚焦的className为android.widget.CheckBox聚焦的控件。
UiSelector x=new UiSelector().fousable(true).className(“android.widget.CheckBox”); UiObject obj = new UiObject(x); obj.click();
(2)操作
案例11-11:点QQ APP。
UiSelector qq=new UiSelector().text(“QQ”); UiObject obj = new UiObject(qq); obj.click();
案例11-12:长按QQ APP。
UiSelector qq=new UiSelector().text(“QQ”); UiObject obj = new UiObject(qq); obj.longClick();
案例11-13:把QQ移动到【560,600】坐标处,40为step。
UiSelector qq=new UiSelector().text(“QQ”); UiObject obj = new UiObject(qq); obj.dragTo(560,600,40);
案例11-14:交换QQ与计算器的位置。
UiSelector qq=new UiSelector().text(“QQ”); UiObject obj = new UiObject(qq); UiSelector calc=new UiSelector().text(“计算器”); UiObject obj1 = new UiObject(calc); obj.drapTo(obj1,40);
案例11-15:按第3个APP向左划屏。
因为instance的编号从0开始,所以这里为2
UiSelector View=new UiSelector().className(“android.view.View”).instance(2); UiObject obj = new UiObject(View); obj.SwipeLeft(10);
案例11-16:模拟短信输入。
UiSelector edit=new UiSelector().className(“android.view.EditText”).instance(0); UiObject obj = new UiObject(edit); obj.setText(“13681732596”); UiSelector edit1=new UiSelector().className(“android.view.EditText”).instance(1); UiObject obj1 = new UiObject(edit1); obj1.setText(“hi”); obj1.clearTextField(); //重新输入 obj1.setText(“hello”);
案例11-17:获得屏幕下方的文本信息。
UiSelector View=new UiSelector().className(“android.view.View”).instance(4); UiObject obj = new UiObject(View); int count = obj.getChildCount(); for(int j=0;j<count;j++){ UiObject child = obj.getChild(new UiSelector().index(j)); System.out.println(child.getText()); }
案例11-18:判断对象是否存在。
UiSelector View=new UiSelector().className(“android.view.View”).instance(2); UiObject obj = new UiObject(View); If (obj.exists ()) { System.out.println(“Object exists!”); }else{ System.out.println(“Object is not exist!”); }
当然可以用assertEquals、assertTrue、assertFalse、assertNull、assertNotNull、assertSame和assertNotSame,参见本篇第8.1.5节介绍。
顾翔凡言:
不是好的工作会给你带来好的心情,而是好的心情会给你带来好的工作。