Android UIAutomator基础学习

简介

UI Automator是Android测试支持库提供的UI测试框架之一,它提供了一组API来构建可以跨应用程序的UI测试,测试代码并不依赖于目标应用的内部实现详情,也就是说测试人员不需要知道将要进行测试的应用程序的具体实现逻辑,只需要调用API来模拟用户UI操作并验证其行为是否正确。

工具

要进行UI Automator测试的设计,也就是实现通过代码来操控UI组件,所以必须得到设备上可见的UI组件,为此android在sdk的tools中提供了uiautomatorviewer工具,它提供了一个方便的界面来查看UI布局层次结构和一些可见的UI组件属性,uiautomatorviewer的使用很简单:

  1. 连接设备并且启动你要查看的应用程序的相应UI
  2. 在sdk的tools目录下找到uiautomatorviewer并启动(我曾出现过sdk的tools目录中没有uiautomatorviewer的情况,重新装了AndroidStudio后在tools中的bin目录下找到了)
  3. 在启动后的uiautomatorviewer界面中点击设备屏幕快照之后就可以查看相应UI组件的属性

 比如在上面的设备快照中可以看到Google的logo是一个ImageView,并且可以看到它的resouce-id,通过这种方式就可以找到组件的text、id、description等信息来使用。

相关类及简单API示例

  •  UiDevice:单例,提供对设备的状态信息的访问(屏幕分辨率、旋转状态、亮灭屏等),还可以模拟设备上的用户操作(按键、滑动、截图、灭屏、唤醒屏幕等)。

获取UiDevice单例实例:

UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());

模拟短按Home键:

device.pressHome();

打开快速设置栏:

device.openQuickSettings();

根据选择器条件获取UiObject对象:

UiObject object = device.findObject(new UiSelector().text("Apps")); //获取一个文本内容为“Apps”的组件

 

  • UiObject: 代表一个具体的组件对象,该对象具有模拟实际操作手机的方法和属性。

模拟点击事件:

UiObject object = device.findObject(new UiSelector().text("Apps")); 
object.click();  //模拟点击事件

 

  • UiSelector:  代表一种搜索条件,通过文本显示,内容描述,类名称和状态信息等属性进行过滤,筛选出符合所有条件的元素,支持链式调用。

指定搜索条件:

UiSelector selector = new UiSelector().text("Apps").className("android.widget.TextView");  //搜索文本内容为“Apps”的TextView

 

  • UiCollection:UiObject的子类,代表元素(组件)条目集合,能够按照一定的条件枚举出容器界面所有符合条件的子元素(组件)。

创建UiCollection实例:

UiCollection allApps = newUiCollection(new UiSelector()
.resourceId("com.google.android.apps.nexuslauncher:id/apps_list_view")); //UiCollection的构造函数需要传入一个UiSelector实例来指定搜索条件
int count = allApp.getChildCount(new UiSelector().textStartsWith("Play")); //在集合中搜索符合文本内容以Play开头的所有组件的数目

可以看到android.support.v7.widget.a这个布局是所有App布局的集合,其中以Play开头的元素一共有4个,所以上面的返回值count=4。

 

  • UiScrollable:UiCollection的子类,比UiCollection多了可以滚动查找组件的功能。
UiScrollable list = new UiScrollable(new UiSelector().className("android.support.v7.widget.RecyclerView"));   
list.scrollTextIntoView("蓝牙");  // 通过滚动RecyclerView找到文本为蓝牙的组件

 

  • Configurator:单例,用于设置一些默认的模拟操作延时,设置后立即生效,并且可以在测试运行期间随时更改。

获取单例实例:

Configurator configurator = Configurator.getInstance();

更改输入文本的延时:

configurator.setKeyInjectionDelay(500);

 

  • InstrumentationRegistry:一个暴露的注册实例,持有instrumentation运行的进程和的参数,还提供获取应用上下文context等方法。

获取当前正在运行的instrumentation实例:

Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();

获取要检测的目标应用程序的上下文context:

Context targetContext = InstrumentationRegistry.getTargetContext();

获取包含instrumentation参数的Bundle:

Bundle bundle = InstrumentationRegistry.getArguments();

 

  • UiObject2:与UiObject同样,代表一个UI组件,与UiObject不同之处在于,它绑定了一个特定的视图实例,当底层视图被销毁或视图变化较大时,需要通过findObject(BySelector)创建一个新实例。在Stack Overflow上有人是这样描述他们之间的不同的:

What they represent

In my words, UiObject2 is a direct representation of a real view that exists on the screen and allows you to take actions on that view. UiObject is a representation of how to find an element that may be on the screen, combined with actions that you can take on that view.

UiObject as the docs say, represents the method to find something on the screen. This means you can re-use a UiObject instance to find another instance of a matching element at a later time.

For example: you can find a toolbar on one screen using the UiObject api (UiDevice.findObject(UiSelector)) to get an instance of UiObject, then open a modal dialog. Once the dialog is dismissed, you can then use the same instance to access the toolbar again.

UiObject2 as the docs say, represents a specific instance of an item on the page, and thus cannot be re-used to find another element at a later time in the same way that a UiObject can be. If you have a UiObject2 instance, you are "guarantee" that a view that matches that instance exists on the page, and the instance can only really be used once.

In the same example as above, after the modal dialog has been dismissed, you would have to use the UiObject2 api (UiDevice.findObject(Until....)) to retrieve a new instance of a UiObject2 that represents the toolbar, because the old one has gone stale by that point.

 

  • Until:提供了构建常用条件对象的工厂方法(包括状态条件UiObject2Condition、搜索条件SearchCondition、事件条件EventCondition)。
  • UiObject2Condition:代表UIObect2满足某个特定状态的条件。
UiObject2Condition condition= Until.textEquals("success"); //文本出现success时的状态条件

 

  • BySelector:代表一种搜索条件,构造方法为包访问权限,因此不能通过new来创建新的条件实例,只能通过By类获取。
  • By:是一个实用程序类,主要功能是提供静态工厂方法来构造BySelector。
BySelector selector = By.clickable(true).text("Bluetooth"); //查找可点击的、文本为Bluetooth的组件

 更多相关类和API用法请参考官网

 使用

 要使用UI Automator需要在build.gradle中添加依赖:

dependencies {
    ...
    androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
}

在Android Studio中创建一个新的工程

由于UI Automator是仪器化测试,也就是需要在Android硬件设备或Android模拟器上运行的测试,所以新建的测试代码应该存放在 /src/androidTest/java/  目录下

UI Automator测试需要运行在Android 4.3(API级别18)或更高版本的设备上。

示例

@RunWith(AndroidJUnit4.class)
@SdkSuppress(minSdkVersion = 18)
public class CustomTest {

    private UiDevice mDevice;
    private String mTelNumber;

    @Before
    public void setUp() throws Exception {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        // 初始化UiDevice实例
        mDevice = UiDevice.getInstance(instrumentation);
        // 按下Home键回到主界面
        mDevice.pressHome();
        // 获取从命令行传入的参数
        Bundle bundle = InstrumentationRegistry.getArguments();
        mTelNumber = bundle.getString("tel.number", "10086");
    }

    @Test
    public void bluetoothTest() throws Exception {
        // 打开蓝牙设置界面
        mDevice.executeShellCommand("am start -n com.android.settings/.Settings$BluetoothSettingsActivity");
        UiObject btSwitch = mDevice.findObject(new UiSelector()
                .resourceId("com.android.settings:id/switch_bar"));
        // 点击蓝牙开关切换蓝牙状态
        btSwitch.clickAndWaitForNewWindow();
    }

    @Test
    public void dialogTest() throws Exception {
        if (mTelNumber != null) {
            mDevice.executeShellCommand("am start -a android.intent.action.CALL tel:" + mTelNumber);
        }
    }
}

注解含义:

  • @RunWith(AndroidJUnit4.class):将测试类声明为一个JUnit 4风格的测试类。

  • @SdkSuppress(minSdkVersion = 18):最小sdk版本为18。

  • @Before:指定包含测试设置操作的代码块,测试类在每次测试之前调用这个代码块,可以有多个@Before方法,但测试类调用这些方法的顺序是不能保证的。

  • @Test:标记测试方法。单个测试类可以包含多个测试方法,每个测试方法都以此注释为前缀。

posted @ 2017-08-08 15:16  明天十八岁  阅读(434)  评论(0编辑  收藏  举报