简述:
UIautomator2 是Google官方针对Android平台推出的一个基于UI自动化测试的框架;适用于跨系统和已安装应用程序的跨应用程序功能UI测试;无需设备root、无需重签名apk、无需配置deviceName、Android 版本等
此处不再过多介绍客户端各种UI自动化框架(Appium、roubotium等)的对比,没有最好用的框架,只有最合适的框架!
UiAutomator2新特性:
- 可获取应用Context对象,通过context可使用Android相关服务于接口(如:获取app信息、wifimanager、操作Activity等)
- 基于Junit4,可直接通过注解方法使用 @RunWith(AndroidJUnit4.class)
- API优化,新增UiObject2、Until、By、BySelector等
- …
循序渐进式:
元素查找:
- 通过Android sdk/tools/uiautomatorviewer.bat 来查找元素位置、层级、属性
元素事件:
- click() 点击
- longClick() 长按
- drag() 拖拽
- swipe() 滑动
- setText() 输入
元素属性:
- id
- className
- textName
- 坐标 (适用于特殊场景:如 widget)
常用类:
- UiDevice 获取设备实例
- UiObject2 获取元素对象
- UiScrollable 处理滚动事件的类
调用封装方法demo
/**
* 调用 baseutil的 getListItem()
* 遍历ListView中的每一个Item,并点击打开对应item,等到新界面出现
*/
private void checkSettingLayout(int index){
for (int i=0;i<4;i++){
getListItem(LIST_VIEW_NAME,2).get(index).clickAndWait(Until.newWindow(),20000);
getListItem(LIST_VIEW_NAME,2).get(i).clickAndWait(Until.newWindow(),20000);
}
}
/**
* 调用 baseutil的 getElement()
* 调用 baseutil 的 isWifi()
* 用户反馈界面,输入反馈内容,并提交
*/
private void checkFeedbackSubmit(){
String descript = "com.test.launcher:id/et_question_descript";
String submit = "com.test.launcher:id/feedback_title_submit";
String etType = "This is a test..." + '\n' + "qwertyuiop+QWERTYUIOP+1234567890+!@#$%^&* + EditText属性 -->输入类型测试" ;
mObject2 = getElement(descript,1);
mObject2.setText(etType);
sleep(2000);
if (!isWifi()){ //wifi未连接
getElement(submit,1).clickAndWait(Until.newWindow(),3000);
Log.i(TAG,"wifi 未连接");
}else {
Log.i(TAG,"wifi 已连接");
//EditText check setText submit
getElement(submit,1).clickAndWait(Until.newWindow(),20000);
sleep(2000);
onClickSetListItem(4);
}
}
常用方法封装
package com.hankhe.autotest.baseutil;
import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.ConnectivityManager;
import android.support.test.InstrumentationRegistry;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.UiObjectNotFoundException;
import android.support.test.uiautomator.UiScrollable;
import android.support.test.uiautomator.UiSelector;
import android.support.test.uiautomator.Until;
import android.util.Log;
import org.junit.Assert;
import java.util.List;
/**
* author: Hank
* create: 2018/5/1
* description: BaseEvent 二次封装uiautomator2 增加代码复用性 Android 4.4+
*/
public class BaseEvent{
private static final String TAG = "Hank";
public UiObject2 uiObject2;
public UiDevice uiDevice;
public Instrumentation instrumentation;
private UiScrollable uiScrollable;
private UiObject uiObject;
private int width,height;
public BaseEvent(){
instrumentation = InstrumentationRegistry.getInstrumentation();
uiDevice = UiDevice.getInstance(instrumentation);
width = uiDevice.getDisplayWidth();
height = uiDevice.getDisplayHeight();
}
public void longClick(String str){
uiObject2 = uiDevice.findObject(By.clazz(str));
uiObject2.longClick();
sleep(2000);
}
//Desktop bottomMenu 中心点
public void longTouch(){
uiDevice.swipe(width/2,height/2,width/2,height/2,100);
}
/**
* 向右滑动
* @param step 步长/速度
* **/
public void swipToRight(int step){
uiDevice.swipe(width/4,height/2,width*3/4,height/2,step);
}
/**
* 向左滑动
* @param step 步长/速度
* **/
public void swipLeft(int step){
uiDevice.swipe(width*3/4,height/2,width/4,height/2,step);
}
/**
* 获取目标元素 + 不加断言(适用于if操作场景 null/equals)
* @param str 元素属性
* @param type 1:byID 2:byClassname 3:byText
*/
public UiObject2 getElement(String str,int type){
UiObject2 uiObject2 = null;
switch (type){
case 1:
uiObject2 = uiDevice.findObject(By.res(str));
break;
case 2:
uiObject2 = uiDevice.findObject(By.clazz(str));
break;
case 3:
uiObject2 = uiDevice.findObject(By.text(str));
break;
default:
break;
}
return uiObject2;
}
/**
* 点击目标元素 + clickAndWait + 断言
* @param str 元素属性
* @param type 1:byID 2:byClassname
*/
public void elementClick(String str,int type){
switch (type){
case 1:
Assert.assertTrue(uiDevice.wait(Until.hasObject(By.res(str)),20000));
uiObject2 = uiDevice.findObject(By.res(str));
break;
case 2:
Assert.assertTrue(uiDevice.wait(Until.hasObject(By.clazz(str)),20000));
uiObject2 = uiDevice.findObject(By.clazz(str));
break;
case 3:
Assert.assertTrue(uiDevice.wait(Until.hasObject(By.text(str)),20000));
uiObject2 = uiDevice.findObject(By.text(str));
break;
default:
break;
}
uiObject2.clickAndWait(Until.newWindow(),3000);//Tiemout 5000
}
/**
* 获取直接子元素 + 断言 (针对Android控件:listview、ScrollView、receclyView、gidView)
* @param str 元素属性
* @param type 元素定位Type 1:byID 2:byClassName
*/
public List<UiObject2> getListItem(String str,int type){
List<UiObject2> listItem = null;
switch (type){
case 1:
Assert.assertTrue(uiDevice.wait(Until.hasObject(By.res(str)),20000));
uiObject2 = uiDevice.findObject(By.res(str));
listItem = uiObject2.getChildren();
break;
case 2:
Assert.assertTrue(uiDevice.wait(Until.hasObject(By.clazz(str)),20000));
uiObject2 = uiDevice.findObject(By.clazz(str));
listItem = uiObject2.getChildren();
break;
default:
break;
}
return listItem;
}
/**
* 获取所有子元素 + 断言 (针对Android控件:listview)
* @param parents 父元素属性
* @param child 子元素(目标元素)
* @param type 元素定位Type 1:byID 2:byClassName
*/
public List<UiObject2> getListAllItem(String parents,String child,int type){
List<UiObject2> allItem = null;
switch (type){
case 1:
Assert.assertTrue(uiDevice.wait(Until.hasObject(By.res(parents)),20000));
uiObject2 = uiDevice.findObject(By.res(parents));
allItem = uiObject2.findObjects(By.res(child));
break;
case 2:
Assert.assertTrue(uiDevice.wait(Until.hasObject(By.clazz(parents)),20000));
uiObject2 = uiDevice.findObject(By.clazz(parents));
allItem = uiObject2.findObjects(By.clazz(child));
break;
default:
break;
}
return allItem;
}
/**
* UiObject
* 获取指定子元素 + 断言 --- Scroll不滚动查询 ,屏幕所展示的非隐藏的 (针对Android控件:ScrollView)
* 针对 没有text/id的 列表元素
* @param parentName 父元素属性
* @param childName 子元素(目标元素)
* @param index 元素
*/
public UiObject getScrollDisableItem(String parentName,String childName,int index){
Assert.assertTrue(uiDevice.wait(Until.hasObject(By.clazz(parentName)),20000));
uiScrollable = new UiScrollable(new UiSelector().className(parentName));
try {
uiObject = uiScrollable.getChildByInstance(new UiSelector().className(childName),index);
} catch (UiObjectNotFoundException e) {
e.printStackTrace();
}
return uiObject;
}
/**
* UiObject
* Scroll滚动查询子元素 + 断言 (针对Android控件:ScrollView)
* @param parentName 父元素属性
* @param childName 子元素(目标元素)
* @param textName 元素text
* @param scroll 是否滚动查询 true:滚动 false:不滚动
*/
public UiObject getScrollEableItem(String parentName,String childName,String textName,boolean scroll){
Assert.assertTrue(uiDevice.wait(Until.hasObject(By.clazz(parentName)),20000));
uiScrollable = new UiScrollable(new UiSelector().className(parentName));
try {
uiObject = uiScrollable.getChildByText(new UiSelector().className(childName),textName,scroll);
} catch (UiObjectNotFoundException e) {
e.printStackTrace();
}
return uiObject;
}
/**
* Scroll 快速滑动 + 断言 (针对Android控件:ScrollView)
* @param parentName 父元素属性
*/
public UiScrollable swipflingScroll(String parentName){
Assert.assertTrue(uiDevice.wait(Until.hasObject(By.clazz(parentName)),20000));
uiScrollable = new UiScrollable(new UiSelector().className(parentName));
return uiScrollable;
}
/**
* Scroll 滚动 + 断言 (针对Android控件:ScrollView)
* @param parentName 父元素属性
*/
public UiScrollable swipScroll(String parentName){
Assert.assertTrue(uiDevice.wait(Until.hasObject(By.clazz(parentName)),20000));
uiScrollable = new UiScrollable(new UiSelector().className(parentName));
uiScrollable.setSwipeDeadZonePercentage(0.1);//不滑动区域百分比
return uiScrollable;
}
/**
* 判断wifi是否开启
* <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
*/
public boolean isWifi() {
Context mContext=InstrumentationRegistry.getInstrumentation().getContext();
boolean state = false;
ConnectivityManager mcM= (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
if(mcM.getActiveNetworkInfo() != null){
state = mcM.getActiveNetworkInfo().isAvailable();
}
return state;
}
/**
* 检测是否有permission Dialog,并执行选择 Allow/Deny
* @param status 1:Allow 2:Deny
* **/
public void checkPermissionMsg(int status){
String sourceId = "com.android.packageinstaller:id/permission_message";
String sourceAllowBtn = "com.android.packageinstaller:id/permission_allow_button";
String sourceDenyBtn = "com.android.packageinstaller:id/permission_deny_button";
uiObject2 = uiDevice.findObject(By.res(sourceId));
if (uiObject2 != null){
switch (status){
case 1:
uiObject2 = uiDevice.findObject(By.res(sourceAllowBtn));
uiObject2.clickAndWait(Until.newWindow(),30000);
Log.i(TAG,"Permission Dialog ---> Allow " );
break;
case 2:
uiObject2 = uiDevice.findObject(By.res(sourceDenyBtn));
uiObject2.clickAndWait(Until.newWindow(),30000);
Log.i(TAG,"Permission Dialog ---> Deny ");
break;
default:
break;
}
}else {
Log.i(TAG,"NO Permission Dialog");
}
}
/**
* 启动指定APP
* @param packageName
* **/
public void doStartApp(String packageName){
Context context=InstrumentationRegistry.getInstrumentation().getContext();
Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(intent);
}
/**
* 启动 所有已安装 APP
* **/
public void doStartAppAll(){
Context context=InstrumentationRegistry.getContext();
PackageManager pm=context.getPackageManager();
List<PackageInfo> packageInfo=pm.getInstalledPackages(0);
List<ApplicationInfo> applicationInfos=pm.getInstalledApplications(0);
Intent mainIntent=new Intent(Intent.ACTION_MAIN,null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> resolveInfos=pm.queryIntentActivities(mainIntent,0);
for(ResolveInfo reinfo:resolveInfos){
String activityName=reinfo.activityInfo.name;
String pkgName=reinfo.activityInfo.packageName;
Intent launchIntent=new Intent();
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
launchIntent.setComponent(new ComponentName(pkgName, activityName));
context.startActivity(launchIntent);
Log.d("启动App",pkgName);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//关闭app 把Activity从栈中移除
uiDevice.pressBack();
uiDevice.pressBack();
uiDevice.pressBack();
uiDevice.pressBack();
}
}
/**
* 睡眠
* @param sleep 单位:ms
* **/
public void sleep(int sleep){
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}