自动化测试 | UI Automator 进阶指南

UI Automator 相关介绍:html

  • 跨应用的用户界面自动化测试
  • 包含在 AndroidX Test(https://developer.android.com/training/testing) 中
  • 支持的 Android 系统:>= Android 4.3 (API level 18)
  • 基于 instrumentation,依赖于 AndroidJUnitRunner 测试运行器

设置 UI Automator(Set up UI Automator)

在编写测试代码前,先确保如下两个配置:
一、测试代码存放位置
二、项目依赖(https://developer.android.com/training/testing/set-up-project)android

(1) 添加 Gradle 依赖(Add Gradle dependencies)git

  • app 目录下的 build.gradle 添加:
allprojects {
    repositories {
        jcenter()
        google()
    }
}
  • dependencies 添加须要的 AndroidX Test Package, 好比:
dependencies {
    ...
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
}
  • 若是测试代码须要基于 junit 的类,好比 Assert 和 TestSuiteLoader,在 android 区块中添加(只须要添加须要用到的 library:https://developer.android.com/training/testing/set-up-project#junit-based-libs):
android {
    ...

    // Gradle automatically adds 'android.test.runner' as a dependency.
    useLibrary 'android.test.runner'

    useLibrary 'android.test.base'
    useLibrary 'android.test.mock'
}

(2) 添加 manifest 声明(Add manifest declarations)
此步骤可选,具体请看 https://developer.android.com/training/testing/set-up-project#add-manifest-declarationsgithub

当前面的配置完成后,进行其余配置:
app下的build.gralde:编程

dependencies {
    ...
    androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
}

当全部配置都完成后,进行被测应用的 UI 组件分析,确保能被识别以及接入控制。微信

检查设备上的用户界面(Inspect the UI on a device)

uiautomatorviewer:app

(1) 启动手机上的被测应用框架

(2) 手机链接电脑ide

(3) 打开 Terminal, 进入目录 <android-sdk>/tools/布局

(4) 运行:uiautomatorviewer

查看应用的用户界面属性:

(1) 点击左上角 "Device Screenshot" 按钮

(2) 左边是 UI 组件,右下半部分是属性,右上半部分是布局层级

(3) 可选功能:点击右上角 "Toggle NAF Nodes" 按钮(黄色三角形,内有感叹号),查看没法被识别/访问的UI组件。---这个功能我都没搞懂怎么用,点击后貌似没效果

确保 activity 可访问(Ensure your activity is accessible)

Android 原生元素具备更好的访问性,利于测试代码的编写,无需额外的支持
若是是自定义 UI 元素,须要(1)建立一个继承自 ExploreByTouchHelper 的实体类(2)经过调用 setAccessibilityDelegate() 将新建立的类的实例和特定的自定义 UI 元素相关联
给自定义视图元素添加无障碍功能的其余参考资料:https://developer.android.com/guide/topics/ui/accessibility/custom-views.html
学习资料 for 提升 Android 的无障碍性/可访问性:https://developer.android.com/guide/topics/ui/accessibility/apps.html

建立一个 UI Automator 测试类(Create a UI Automator test class)

UI Automator 测试类的写法和 JUnit 4 测试类的写法是同样的。
JUnit 4 测试类的学习资料:https://developer.android.com/training/testing/unit-testing/instrumented-unit-tests.html#build

在测试类开头添加注解:@RunWith(AndroidJUnit4.class)
同时,明确 AndroidX Test 中的 AndroidJUnitRunner 类为默认的测试运行器。这个步骤的详细描述:https://developer.android.com/training/testing/ui-testing/uiautomator-testing.html#run

在 UI Automator 测试类中执行如下编程模型:

  1. 获取一个 UiDevice 对象去接入测试设备,调用 getInstance() 方法,传入 Instrumentation 对象做为参数。
  2. 经过 UiObject 对象调用 findObject() 方法接入显示在设备上的 UI 组件(例如,当前手机屏幕显示的用户界面)。
  3. 经过调用 UiObject 方法在 UI 组件上模拟一个交互的动做。例如,调用 performMultiPointerGesture() 方法模拟多指触控,调用 setText() 方法编辑文本框。当测试包含多个 UI 组件或者更加复杂的操做序列时,在第二步和第三步中可重复调用各类 API.
  4. 当执行完这些用户交互的动做后,检查返回的结果是否符合预期。
    这些步骤在如下章节会讲的更加详细。

访问用户界面组件 (Access UI components)

UiDevice: 接入和控制设备状态的首要方法,可执行设备级别的行为,例如改变屏幕旋转方向、按下硬件按钮、以及点击 home 和 menu 键。

从设备的主屏幕开始测试是一个好的实践。在主屏幕(或者其余你在设备上选定的开始位置),能够调用 UI Automator API 提供的方法和指定的 UI 元素进行交互。

如下代码片断展现了如何获取一个 UiDevice 的实例以及模拟按下 home 键的操做:

import org.junit.Before;
import androidx.test.runner.AndroidJUnit4;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.Until;
...
@RunWith(AndroidJUnit4.class)
@SdkSuppress(minSdkVersion = 18)
public class ChangeTextBehaviorTest {

    private static final String BASIC_SAMPLE_PACKAGE
            = "com.example.android.testing.uiautomator.BasicSample";
    private static final int LAUNCH_TIMEOUT = 5000;
    private static final String STRING_TO_BE_TYPED = "UiAutomator";
    private UiDevice device;

    @Before
    public void startMainActivityFromHomeScreen() {
        // Initialize UiDevice instance
        device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());

        // Start from the home screen
        device.pressHome();

        // Wait for launcher
        final String launcherPackage = device.getLauncherPackageName();
        assertThat(launcherPackage, notNullValue());
        device.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)),
                LAUNCH_TIMEOUT);

        // Launch the app
        Context context = ApplicationProvider.getApplicationContext();
        final Intent intent = context.getPackageManager()
                .getLaunchIntentForPackage(BASIC_SAMPLE_PACKAGE);
        // Clear out any previous instances
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
        context.startActivity(intent);

        // Wait for the app to appear
        device.wait(Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)),
                LAUNCH_TIMEOUT);
    }
}

示例代码中的声明:@SdkSuppress(minSdkVersion = 18), 帮助肯定测试只运行在 Android4.3(API level 18)或更高级别的设备上。(UI Automator 框架要求的)

  • findObject()
  • UiObject
UiObject cancelButton = device.findObject(new UiSelector()
        .text("Cancel")
        .className("android.widget.Button"));
UiObject okButton = device.findObject(new UiSelector()
        .text("OK")
        .className("android.widget.Button"));

// Simulate a user-click on the OK button, if found.
if(okButton.exists() && okButton.isEnabled()) {
    okButton.click();
}

指定一个选择器(Specify a selector)

UiSelector 类:在当前显示的用户界面中查询一个特定的元素。

  • childSelector()
  • UiAutomatorObjectNotFoundException
UiObject appItem = device.findObject(new UiSelector()
        .className("android.widget.ListView")
        .instance(0)
        .childSelector(new UiSelector()
        .text("Apps")));

tips:

  • 若是在一个页面上找到一个以上的相同元素,自动返回第一个匹配的元素做为目标 UiObject.
  • 能够经过整合多个属性来缩小搜索范围。
  • 若是没有找到目标元素,抛出 UiAutomatorObjectNotFoundException 异常。
  • 可使用 childSelector() 方法缩小多个 UiSelector 实例范围。
  • 若是有 Resource ID, 用这个代替 text 和 content-descripter.
  • text 元素比较脆弱,有多种缘由可能致使测试失败。(好比:多语言)
    在选择区域中去明确一个对象状态是很是有用的。好比:选择一个已选中的列表以进行取消选中状态,调用 checked() 方法,将参数设为 "true".

执行动做(Perform actions)

当获取 UiObject 对象后,能够调用 UiObject 类中的方法在其上执行相应操做:

  • click(): 点击
  • dragTo(): 拖动
  • setText(): 设置文本
  • clearTextField(): 清空文本
  • swipeUp(): 向上滑动
  • swipeDown(): 向下滑动
  • swipeLeft(): 向左滑动
  • swipeRight(): 向右滑动

经过 getContext() 方法获取到 Context 后,能够进行发送 Intent 或者启动 Activity 的操做。

public void setUp() {
    ...

    // Launch a simple calculator app
    Context context = getInstrumentation().getContext();
    Intent intent = context.getPackageManager()
            .getLaunchIntentForPackage(CALC_PACKAGE);
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);

    // Clear out any previous instances
    context.startActivity(intent);
    device.wait(Until.hasObject(By.pkg(CALC_PACKAGE).depth(0)), TIMEOUT);
}

在集合上执行动做(Perform actions on collections)

  • UiCollection 类:在一个 item 的集合上模拟用户操做(例如,歌曲列表或者邮件列表)。
    如何建立一个 UiCollection 对象:明确一个搜索UI容器或者其余子 UI 元素集合的 UiSelector. 例如,包含子 UI 元素的 layout 视图。
UiCollection videos = new UiCollection(new UiSelector()
        .className("android.widget.FrameLayout"));

// Retrieve the number of videos in this collection:
int count = videos.getChildCount(new UiSelector()
        .className("android.widget.LinearLayout"));

// Find a specific video and simulate a user-click on it
UiObject video = videos.getChildByText(new UiSelector()
        .className("android.widget.LinearLayout"), "Cute Baby Laughing");
video.click();

// Simulate selecting a checkbox that is associated with the video
UiObject checkBox = video.getChild(new UiSelector()
        .className("android.widget.Checkbox"));
if(!checkBox.isSelected()) checkbox.click();

在可滚动视图中执行动做(Perform actions on scrollable views)

  • UiScrollable 类:在手机屏幕上模拟垂直或者水平的滚动操做。这个类适用于当须要找到屏幕外的 UI 元素时,能够经过滚动操做将这个 UI 元素带到屏幕内。
UiScrollable settingsItem = new UiScrollable(new UiSelector()
        .className("android.widget.ListView"));
UiObject about = settingsItem.getChildByText(new UiSelector()
        .className("android.widget.LinearLayout"), "About tablet");
about.click();

验证结果(Verify results)

InstrumentationTestCase 继承自 TestCase,可使用标准的 JUnit Assert 方法进行结果验证。
如下代码片断展现了如何验证计算器加法:

private static final String CALC_PACKAGE = "com.myexample.calc";
public void testTwoPlusThreeEqualsFive() {
    // Enter an equation: 2 + 3 = ?
    device.findObject(new UiSelector()
            .packageName(CALC_PACKAGE).resourceId("two")).click();
    device.findObject(new UiSelector()
            .packageName(CALC_PACKAGE).resourceId("plus")).click();
    device.findObject(new UiSelector()
            .packageName(CALC_PACKAGE).resourceId("three")).click();
    device.findObject(new UiSelector()
            .packageName(CALC_PACKAGE).resourceId("equals")).click();
    // Verify the result = 5
    UiObject result = device.findObject(By.res(CALC_PACKAGE, "result"));
    assertEquals("5", result.getText());
}

在设备或虚拟机上运行 UI Automator 测试用例(Run UI Automator tests on a device or emulator)

能够经过 Android Studio 或者命令行运行 UI Automator tests. 确保项目的默认 instrumentation runner 是 AndroidJUnitRunner.

参考资料(Additional resources)

Samples:
https://github.com/googlesamples/android-testing/tree/master/ui/uiautomator/BasicSample 基础的UI Automator 示例代码
Codelabs:
https://codelabs.developers.google.com/codelabs/android-testing/index.html


欢迎关注微信公众号"测试开发Stack"

相关文章
相关标签/搜索