Uiautomator是由谷歌推出的用于UI自动化测试的工具,花了一天时间研究了下,总体来说还是非常简单,api非常简洁,今天主要帮助大家入门。对于安卓环境搭建我就不多讲了,我们直接进入Uiautomator讲解。
1 创建工程
直接用android studio创建一个空工程。启动studio,点击start a new Android Studio project,在application name处填上工程名,点击 Next, 在target android devices页面,选择默认最小的sdk,点next,在点add no activity,点finish,这样就创建好了一个新工程。
2 添加配置
在Module:app里的build.gradle的dependencies方法中,添加
implementation 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'。复制代码
最后点击refresh all gradle按钮,更新uiautomator
。
3 基础api
主要用到的两个对象device(直接操作设备),UiObject2(ui节点对象)。所有的ui测试都是基于设备或者节点对象,进行相关操作。只要吃透了这些方法的用法,写代码就可以信手拈来。
1.UiDevice使用
- UiDevice按键
返回值 | 方法名 | 说明 |
boolean | pressBack() | 模拟短按返回back键 |
boolean | pressDPadCenter() | 模拟按轨迹球中点按键 |
boolean | pressSPadDown() | 模拟轨迹球向下按键 |
boolean | pressDPadLeft() | 模拟轨迹球向左按键 |
boolean | pressDPadRight() | 模拟轨迹球向右按键 |
boolean | pressDPadUp() | 模拟轨迹球向上按键 |
boolean | pressDelete() | 模拟短按删除delete按键 |
boolean | pressEnter() | 模拟短按回车enter键 |
boolean | pressHome() | 模拟短按home键 |
boolean | pressKeyCode(int keyCode,int metaState) | 模拟短按键盘代码keycode |
boolean | pressKetCode(int keyCode) | 模拟短按键盘代码keycode |
boolean | pressMenu() | 模拟短按menu键 |
boolean | pressRecentApps() | 模拟短按最近使用程序 |
boolean | pressSearch() | 模拟短按搜索键 |
UiDevice.getInstance().pressBack();//点击返回键
UiDevice.getInstance().pressHome();//点击Home键
UiDevice.pressKeyCode(KeyEvent.KEYCODE);//键盘按键复制代码
- 手势操作
click() 点击
dragTo() 拖动当前元素
swipe() 向上下左右滑动复制代码
其它高级用法暂时不做讨论,需要用到的话直接调用即可,例如唤醒屏幕,截屏等
2.UiObject2使用
基础动作模拟API
返回 | API | 说明 |
---|---|---|
void | clear() | 清除编辑框中的内容 |
void | click() | 点击一个对象 |
<R> R | clickAndWait(EventCondition<R> condition, long timeout) | 点击一个对象,然后等待在超时时间内条件成立则通过,否则抛出异常 |
void | drag(Point dest, int speed) | 自定义速度拖拽一个对象到指定位置,速度:像素/秒 |
void | drag(Point dest) | 拖拽一个对象到指定位置 |
void | longClick() | 长时间点击对象 |
boolean | scroll(Direction direction, float percent) | 滚动操作 |
boolean | scroll(Direction direction, float percent, int speed) | 自定义速度的滚动操作 |
void | setText(String text) | 设置文本内容 |
手势API
返回 | API | 说明 |
---|---|---|
void | pinchClose(float percent, int speed) | 自定义速度关闭手势 |
void | pinchOpen(float percent, int speed) | 自定义速度打开手势 |
void | pinchOpen(float percent) | 打开手势 |
boolean | fling(Direction direction) | 滑动手势 |
boolean | fling(Direction direction, int speed) | 自定义速度滑动手势 |
void | swipe(Direction direction, float percent, int speed) | 自定义速度滑动手势 |
void | swipe(Direction direction, float percent) | 自滑动手势 |
组件属性API
返回 | API | 说明 |
---|---|---|
String | getApplicationPackage() | 返回应用包名 |
String | getClassName() | 返回对象类名 |
String | getContentDescription() | 返回内容描述 |
String | getResourceName() | 返回资源id |
String | getText() | 返回文本 |
Rect | getVisibleBounds() | 返回对象可见范围内的屏幕坐标 |
Point | getVisibleCenter() | 返回可见范围的中心 |
boolean | isCheckable() | 返回Checkable属性 |
boolean | isChecked() | 返回Checked属性 |
boolean | isClickable() | 返回Clickable属性 |
boolean | isEnabled() | 返回Enabled属性 |
boolean | isFocusable() | 返回Focusable属性 |
boolean | isFocused() | 返回isFocused属性 |
boolean | isLongClickable() | 返回LongClickable属性 |
boolean | isScrollable() | 返回Scrollable属性 |
boolean | isSelected() | 返回Selected属性 |
层级关系API
返回 | API | 说明 |
---|---|---|
UiObject2 | findObject(BySelector selector) | 在该对象层级之下,返回第一个与条件匹配的对象 |
List<UiObject2> | findObjects(BySelector selector) | 在该对象层级之下,返回所有匹配的对象 |
List<UiObject2> | getChildren() | 返回该对象的所有子元素的集合 |
UiObject2 | getParent() | 返回该对象的父元素 |
int | getChildCount() | 返回该对象的直接子元素的数量 |
条件判断API
返回 | API | 说明 |
---|---|---|
boolean | equals(Object object) | 比较两个对象是否相等 |
int | hashCode() | 获取对象的hashCode |
boolean | hasObject(BySelector selector) | 返回对象是否存在 |
<R> R | wait(UiObject2Condition<R> condition, long timeout) | 等待的条件得到满足 |
<R> R | wait(SearchCondition<R> condition, long timeout) | 等待的条件得到满足 |
注意:进行手势操作的时候,最好需要进行Thread.sleep()延迟操作,执行动画以及更新ui操作都是需要时间的,如果执行下个方法查找节点时页面还未及时更新,则会出现获取对象失败导致直接跑出异常。此时需要用到hasObject()判断对象是否存在,可以处理多种状态的逻辑判断,例如登录和未登陆显示的ui不一样,则需要根据节点判断当前是否处于登录状态。
4 在androidTest目录下写代码
接下来我们就简单的写个case来熟悉下基本操作,就做个简单的打开微信(已登录状态下,未登陆的情况大家熟练后在自行编写),然后发送聊天对话。
package com.appiumtest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.Direction;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;
import android.util.Log;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Instrumented test, which will execute on an Android device.
*
* author tangge by 2018-04-16
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest2 {
private UiDevice device = null;
private String packageName = "com.tencent.mm";
@Test
public void startUp(){
Context appContext = InstrumentationRegistry.getTargetContext();
try {
useAppContext(appContext);
} catch (Exception e) {
e.printStackTrace();
Log.e("UiDevice",e.getMessage());
}
}
private void useAppContext(Context context) throws Exception {
// Context of the app under test.
device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
device.pressHome();
startApp(context);
//已登录状态,点击列表中第一个对话
sleep(10);
swipe("b2m",Direction.LEFT);//左滑
swipe("b2m",Direction.RIGHT);//右滑
clickListView("c3p",0,1);//点击第一个对话
click("aaf");//点击输入框
getObjById("aaf",1).setText("你好");
click("aal",1);//发送
click("h9",1);//点击返回
}
//启动APP
private void startApp(Context context){
Intent intent = new Intent();
intent.setComponent(new ComponentName(packageName, packageName+".ui.LauncherUI"));
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(intent);
}
private UiObject2 getObjById(String id,float sencend){
return device.wait(Until.findObject(By.res(packageName, id)), sencend > 0 ? (long) (sencend * 1000) : 200);
}
//点击
private void click(String id) throws Exception{
click(id,0);
}
//延迟点击
private void click(String id, float sencend) throws Exception{
if(exitObj(id,sencend)){
getObjById(id,0).click();
}
}
//延迟点击
private void setText(String id, String text ,float sencend) throws Exception{
if(exitObj(id,sencend)){
getObjById(id,0).setText(text);
}
}
//判断是否存在该对象
private boolean exitObj(String id, float sencend) throws Exception{
sleep(sencend);
return device.hasObject(By.res(packageName,id));
}
//判断是否存在该对象
private boolean exitObjByText(String text, float sencend) throws Exception{
sleep(sencend);
return device.hasObject(By.text(text));
}
//延迟2秒滑动
private void swipe(String id, Direction direction) {
getObjById(id,3).swipe(direction,0.8f,4000);
}
//点击列表第几个元素
private void clickListView(String id,int position,float sencend) {
//点击第几个
getObjById(id,sencend).getChildren().get(position).click();
}
private void sleep(float sencend) throws Exception{
Thread.sleep(sencend > 0 ? (long) (sencend * 1000) : 500);
}
}复制代码
右击测试文件,点击运行即可。代码中已经封装了点击,滑动,判断对象是否存在,点击list列表等方法。希望大家能通过这个简单case快速入门,然后编写出完整的一套测试流程。源码附上download.youkuaiyun.com/download/qq…