android自动化测试(N):UiAutomator用法

它是一个Android自动化测试框架,是谷歌在Android4.1版本发布时推出的一款用Java编写的UI测试框架。它只能用于UI也就是黑盒方面的测试。所以UiAutomator只能运行在4.1以后的版本中。其最大的特点就是可以跨进程操作,我们可以使用uiautomator框架提供的一些方便的API来对安卓应用进行一系列的自动化测试操作,如点击、滑动、键盘输入、长按以及常用的断言方法等。可以替代以前繁琐的手工测试。

下面总结一下该框架的几个优点:

  1. Google自家推出的,其稳定性和后续的维护更新可以得到保障,运行时也有更多的权限。
  2. 可以跨进程操作,这点比起其它基于instrumentation框架的自动化工具如Robotium是无法直接做到的。
  3. 运行速度快。 缺点:
  4. 不支持Android4.1以下的版本。
  5. 不支持Webview,所以一般无法对浏览器应用进行测试。
UiAutomator 框架原理分析:

首先,UiAutomator是Google参考微软的UiAutomation提供的一套用在Android上的自动化测试框架。基于Android AccessilibilityService提供。那么至于什么是AccessilibilityService,在这里简单介绍下:Android AccessilibilityService,是一个可访问服务,它是一个为增强用户界面并帮助残疾用户的应用程序,或者用户可能无法完全与设备的交互。举个简单的例子,假如一个用户在开车。那么用户就有可能需要添加额外的或者替代的用户反馈方式。其应用方式一般有两种:

第一种方法是:UiAutomatorView + monkey。它与hierachyview + monkey差不多。其区别是:UiAutomatorView通过ADB向设备侧发送一个dump命令,而不是建立一个socket,下载一个包含当前界面控件布局信息的xml文件。相比较hierachyview下载的内容而言,该文件小很多。因此,从效率上讲,这种方法比第一种应用模式快很多。

第二种方法是: 直接调用UiAutomator框架对外提供的API,主要有UiDevice、UiSelector、UiObject和 UiScrollable等。其原理与第一种方式即HierachyView + Monkey差不多。其过程大致是:首先,UiAutomator测试框架通过Accessibilityservice,获取当前窗口的控件层次关系及属性信息,并查找到目标控件。若是点击事件,则计算出该控件的中心点坐标。其次,UiAutomator通过 InputManager.getInstance().injectInputEvent隐藏接口来注入用户事件(点击、输入类操作),从而实现跨进程自动化的目的。

UiAutomatorTestCase :这个类是继承自Junit TestCase (Junit),对外提供setup、teardown等,以便初始化用例、清除环境等。所以我们在编写的UiAutomator 的脚本时一般都要继承这个类,这样就可以直接使用它的一些方法和Junit单元测试框架中的Assert断言机制。

UIAutomator2.0

We’re pleased to announce the release of UIAutomator 2.0! This version is a significant update from the previous release. Most importantly, UI Automator is now based on Android Instrumentation and you can build and run tests with the ‘./gradlew connectedCheck’ command.

UiAutomator2.0的jar包并不是在以前SDK/platforms/android-19/下。现在我们要这么做

  1. 通过Android SDK Manager中的 Android Support Repository 项进行安装

  2. 下载下来的jar包的路径为/extras/android/m2repository

  3. 新建一个android项目,编写一个简单的应用

  4. 在build.gradle中配置依赖项:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    dependencies {
    androidTestCompile 'com.android.support.test:runner:0.3'
    // Set this dependency to use JUnit 4 rules
    androidTestCompile 'com.android.support.test:rules:0.3'
    // Set this dependency to build and run Espresso tests
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2'
    // Set this dependency to build and run UI Automator tests
    androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.1'
    }
  5. 设置AndroidJunitRunner为默认的testInstrumentationRunner

    1
    2
    3
    4
    5
    android {
    defaultConfig {
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    }
  6. 编写测试代码,在androidTest目录下面新建测试类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    public class LoginTest extends InstrumentationTestCase {

    protected UiDevice device = null;
    protected String appName = "magicCard";

    public void runApp(String appName) throws UiObjectNotFoundException, RemoteException {
    device = UiDevice.getInstance(getInstrumentation());
    device.pressHome();
    device.waitForWindowUpdate("", 2000);

    UiObject2 allAppsButton = device.findObject(By.desc("Apps"));
    allAppsButton.click();
    device.waitForWindowUpdate("", 2000);

    UiScrollable appViews = new UiScrollable(new UiSelector().scrollable(true));
    appViews.setAsHorizontalList();

    UiObject settingsApp = appViews.getChildByText(new UiSelector().className(TextView.class.getName()), appName);
    settingsApp.clickAndWaitForNewWindow();

    assertTrue("Unable to detect app", settingsApp != null);
    }

    @Override
    public void setUp() throws RemoteException, UiObjectNotFoundException {
    this.runApp(appName);
    }

    @Override
    public void tearDown() throws RemoteException, UiObjectNotFoundException {
    //Empty for the moment
    }

    public void testUS1() {
    UiObject2 usernameLabel = device.findObject(By.clazz(TextView.class.getName()).text("Username"));
    assertTrue("Username label not found", usernameLabel != null);
    }
    }

基于Instrument的方便一点就是不需要remote debug的方式进行调试。并且做参数化之类的也方便了很多。 2.0不用再继承UiAutomatorTestCase,但却需要继承InstrumentationTestCase。

获取设备的方式也变化了,UiDevice.getInstance(getInstrumentation()) 这才是正确的使用方法。之前常用的两种方式都不再可行。

可以通过如下的adb命令调用

1
adb shell am instrument -w -r   -e debug false -e class com.cxq.uiautomatordemo.UiTest com.cxq.uiautomatordemo.test/android.test.InstrumentationTestRunner

在dependencies中用到了compile、testCompile、androidTestCompile三种依赖方式,让我们来看看他们有什么区别:

compile:参与编译,并且会打包到debug/release apk中。 testCompile:只参与单元测试编译,不会打包到debug/release apk包中,不需要设备支持。 androidTestCompile:只参与UI测试编译,不会打包到debug/release apk包中,需要设备支持。

除此之外还有Provided、APK、Debug compile和Release compile:

Provided:只参与编译,不会打包到debug/release apk中。 APK:不参与编译,只会打包到debug/release apk中。 Debug compile:只参与debug编译,只会打包到debug apk中。 Release compile:只参与release编译,只会打包到release apk中。

UIAutomator1.0
  1. 新建Java工程

  2. 导入lib包 android.jar 和 uiautomator.jar ,选中点击右键Add to buildPath

  3. 新建测试类demo

    1
    public class Demo extends UiAutomatorTestCase{}
  4. 写测试方法A,B,C(testcase)

  5. 编译运行:

    1. <android-sdk>/tools/android create uitest-project -n <name> -t 1 -p <path> 说明一下各个参数的作用,如果已经将android sdk的路径配置到了系统的path中,输入命令“android create uitest-project”就可以查看到相应的帮助
  • -n --name : Project name. 就是在eclipse中创建的项目的名字。
  • -t --target : Target ID of the new project. [required] 这个id是本机上android targets的id,可以通过命令 “android list”来查询,得到结果,选择android-17以上版本前面所对应的id,运行完成后,工作空间下生成文件build.xml

5.2. 修改build.xml 将help改为build

1
2
<?xml version="1.0" encoding="UTF-8"?>            
<project name="demo1" default="build">

5.3.在build.xml上点击右键,选择“Run As” -> “Ant Build”,编译成功,在工作空间bin下生成一个jar包demo.jar

5.4. adb push demo.jar /data/local/tmp/

5.5. adb shell uiautomator runtest demo.jar -c A -c B -c C(可指定多个testcase,不指定则运行所有)

uiautomator的help帮助: 支持三个子命令:rutest/dump/events

  • runtest命令-c指定要测试的class文件,用逗号分开,没有指定的话默认执行测试脚本jar包的所有测试类.注意用户可以以格式$class/$method来指定只是测试该class的某一个指定的方法
  • runtest命令-e参数可以指定是否开启debug模式
  • runtest命令-e参数可以指定test runner,不指定就使用系统默认。我自己从来没有指定过
  • runtest命令-e参数还可以通过键值对来指定传递给测试类的参数

UiAutomator2改进

  1. 基于 Instrumentation,可以获取应用Context,使用 Android服务及接口

  2. 基于 Junit4,测试用例无需继承于任何父类,方法名不限,使用注解 Annotation进行

  3. UI执行效率比 1.0 快,测试执行可使用AndroidJunit 方式及gradle 方式

  4. API 更新,新增UiObject2、Until、By、BySelector等:APIFor UI Automator

  5. Log 输出变更,以往使用System.out.print输出流回显至执行端,2.0 输出至Logcat

AndroidJUnitRunner AndroidJUnitRunner InstrumentationTestRunner Fundamentals of Testing UI Automator androidx.test Test UI for multiple apps Instrumentation

坚持原创技术分享,您的支持将鼓励我继续创作!