翻译官方文档-自动化UI测试

官方文档链接:https://developer.android.google.cn/training/testing/ui-testing/index.html

1.前言


用户界面(UI)测试可以确保应用程序满足其功能需求,同时质量达到一个高的标准,这样才更可能成功地被用户接受。有一种UI测试,简单地让测试人员在目标应用程序上执行一系列用户操作,来验证它响应的正确性。然而,这种手动操作耗时、乏味且容易出错。更有效的方法是编写UI测试,以便自动执行用户操作,从而可重复地、快速地、可靠地运行测试。

注意:强烈建议使用Android Studio构建测试应用程序,因为它提供项目设置、库依赖和便捷打包,文章也将基于使用Android Studio来描述。

通过Android Studio进行自动化UI测试,须在独立的安卓测试目录(src/androidTest/java)下实现测试代码。首先是安卓Gradle插件基于测试代码构建测试应用程序,接着在同一设备上分别加载测试及目标应用程序。在测试代码中,可以使用UI测试框架对目标应用程序模拟用户交互,以便具体使用场景下执行测试任务。

为了测试安卓应用程序,通常会创建这些类型的自动化UI测试:

  • 仅单个应用程序的UI测试:这种类型的测试,可以检查目标应用程序在响应用户界面交互时返回正确的UI,即当用户执行特定操作或输入行为时,验证目标应用程序响应是否符合预期。类似Espresso的UI测试框架,以编程的方式模拟用户操作,来测试复杂的应用程序内交互。
  • 跨多个应用程序的UI测试:这种类型的测试,验证不同用户应用程序或用户与系统应用程序之间,正确的交互行为。例如,可能想要测试相机(或默认的安卓相册)通过第三方社交媒体软件,正确分享图像。类似UI Automator的UI测试框架,支持跨应用交互。

下面将介绍如何使用安卓测试支持库中的工具和APIs来构建这些类型的自动化测试。在此之前,必须安装安卓测试支持库,请参阅下载安卓测试支持库

2.仅单个应用程序的UI测试


测试单个应用程序内的用户交互,有助于确保用户不会遇到意外的结果或与应用程序交互时体验不佳。如果需要验证应用程序的UI是否正常运行,应该养成创建用户界面(UI)测试的习惯。

安卓测试支持库中的Espresso测试框架,为编写UI测试来模拟单个目标应用程序的用户交互提供APIs,可以运行于Android 2.3.3(API 10)及以上版本的设备。使用它的关键好处是,能够自动同步被测试应用程序UI的测试行为,即发现主线程空闲时,能够在适当的时候运行测试命令,提高测试的可靠性。同时,也使开发不用添加任务执行时机相关的代码,比如Thread.sleep()。Espresso测试框架是基于设备的API,且与AndroidJUnitRunner测试运行器一起使用。

2.1.设置Espresso

使用Espresso构建UI测试之前,确保配置好测试源代码的位置和项目依赖。在安卓app模块的build.gradle文件中,必须设置对Espresso库的依赖引用:

dependencies {
    // Other dependencies ...
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
}

关闭测试设备的动画:任由测试设备上的系统动画处于打开状态,可能会导致意外的结果或测试失败。通过打开开发者选项,在设置中关闭动画及以下所有选项:

  • 窗口动画缩放
  • 过渡动画缩放
  • 动画时长缩放

除了Espresso提供的核心API外,还想让项目使用到更多的特性,请参阅资源

2.2.创建Espresso测试类

若想创建Espresso测试,需按照下面开发模式编写Java类:

  • 通过调用onView()方法找到Activity中想要测试的UI组件(例如,应用程序的登录按钮),或者onData()方法获取AdapterView的子项。
  • 通过调用ViewInteraction.perform()DataInteraction.perform()方法,模拟作用于UI组件的特定用户交互和传递用户行为(例如,点击登录按钮)。若对同一个UI组件连续执行多个操作,使用逗号分隔的方式将它们列在方法参数中。
  • 必要时重复上述步骤,来模拟用户在目标应用程序上跨界面操作。
  • 使用ViewAssertions的方法来检查,当这些用户交互执行后,UI响应是否符合预期状态或行为。

在后面的几节中将包含更详细的介绍,此处先提供代码片段展示测试类如何调用这个基本工作流:

onView(withId(R.id.my_view))            // withId(R.id.my_view) is a ViewMatcher
        .perform(click())               // click() is a ViewAction
        .check(matches(isDisplayed())); // matches(isDisplayed()) is a ViewAssertion
2.3.配合ActivityTestRule

本节描述如何创建新的符合JUnit 4样式的Espresso测试,同时使用ActivityTestRule减少需要写的样板代码的数量。ActivityTestRule可以让测试框架在每个被@Test注解的测试方法和任何@Before注解的方法调用之前,启动被测试的Activity。还能让框架在测试结束后,处理Activity资源释放,并运行所有@After注解的方法。

package com.example.android.testing.espresso.BasicSample;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
...

@RunWith(AndroidJUnit4.class)
@LargeTest
public class ChangeTextBehaviorTest {

    private String mStringToBetyped;

    @Rule
    public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(
            MainActivity.class);

    @Before
    public void initValidString() {
        // Specify a valid string.
        mStringToBetyped = "Espresso";
    }

    @Test
    public void changeText_sameActivity() {
        // Type text and then press the button.
        onView(withId(R.id.editTextUserInput))
                .perform(typeText(mStringToBetyped), closeSoftKeyboard());
        onView(withId(R.id.changeTextBt)).perform(click());

        // Check that the text was changed.
        onView(withId(R.id.textToBeChanged))
                .check(matches(withText(mStringToBetyped)));
    }
}
2.4.运行Espresso测试

可以从Android Studio或命令行运行Espresso测试,确认项目已指定AndroidJUnitRunner作为默认设备运行器。

3.Espresso使用详解


3.1.访问UI组件

在Espresso与被测试应用程序交互之前,必须先指定UI组件或视图控件。Espresso也支持给应用中的特定视图控件和适配器使用Hamcrest匹配器。

为了找到视图控件,调用onView()方法,并传入目标视图特有的匹配器,更详细的描述请参阅指定视图匹配onView()方法返回允许与视图控件测试交互的ViewInteraction对象,但想要定位RecyclerView布局中的视图控件,调用此方法可能不起作用。因此,可以按照定位AdapterView中视图的指示操作。

注意:Espresso的onView()方法不检查指定的视图是否有效,而是使用提供的匹配器仅搜索当前视图层级。如果没有发现匹配的,此方法会抛出NoMatchingViewException异常。

下面的代码片段展示如何编写测试代码,实现访问EditText控件、输入文本字符串、关闭虚拟键盘和执行按钮点击。

public void testChangeText_sameActivity() {
    // Type text and then press the button.
    onView(withId(R.id.editTextUserInput))
            .perform(typeText(STRING_TO_BE_TYPED), closeSoftKeyboard());
    onView(withId(R.id.changeTextButton)).perform(click());

    // Check that the text was changed.
    ...
}
3.2.指定视图匹配器

通过使用这些方法可以指定视图匹配器:

  • 调用ViewMatchers类中的方法,例如,通过查找显示的文本字符串来定位视图控件,如下所示:

    onView(withText("Sign-in"));
    

    同样也可以调用withId()方法,并提供视图控件的资源ID(R.id),如下面的例子所示:

    onView(withId(R.id.button_signin));
    

    安卓资源IDs不能保证是唯一的。如果测试中尝试匹配被多个视图控件使用的同一资源ID,Espresso会抛出AmbiguousViewMatcherException异常。

  • 使用Hamcrest的Matchers类。可以使用allOf()方法去合并多个匹配器,比如containsString()instanceOf()方法。这种方法允许更仔细地过滤匹配结果,如下面的例子所示:

    onView(allOf(withId(R.id.button_signin), withText("Sign-in")));
    

    可以使用not关键字来过滤出不符合匹配器的视图,如下面的例子所示:

    onView(allOf(withId(R.id.button_signin), not(withText("Sign-out"))));
    

    在测试中使用这些方法,需导入org.hamcrest.Matchers包。要了解更多关于Hamcrest的匹配,请参阅官方网站

为了提高Espresso测试的性能,请指定找到目标视图控件需要的最小匹配信息。例如,如果视图控件通过自己的描述文本可唯一确定,就不需要再指定它属于TextView的实例。

3.3.定位AdapterView中视图

AdapterView控件中的视图,由子视图在运行时动态排列而成。如果想要测试的目标视图在AdapterView(例如ListView,GridView或Spinner)内,onView()方法可能失效,因为当前展示的视图层次结构可能只加载了部分子视图。这时,可以调用onData()方法获取DataInteraction对象来访问目标视图元素,由Espresso负责加载该元素到当前视图层次结构,以及滚动到它所在位置并将它放入焦点。

注意:Espresso的onData()方法不检查指定的视图是否有效,而是使用提供的匹配器仅搜索当前视图层级。如果没有发现匹配的,此方法会抛出NoMatchingViewException异常。

下面的代码片段展示,如何使用onData()方法结合Hamcrest匹配器搜索列表中包含特定字符串的指定项。在这个例子中,LongListActivity类通过SimpleAdapter显示字符串列表。

onData(allOf(is(instanceOf(Map.class)),
        hasEntry(equalTo(LongListActivity.ROW_TEXT), is("test input")));
3.4.执行交互

调用ViewInteraction.perform()DataInteraction.perform()方法来模拟对UI组件的用户交互,必须传入一个或多个ViewAction对象作为参数。Espresso按给定顺序依次触发每个交互,并在主线程中执行它们。ViewActions类提供一系列辅助方法来指定通用交互,直接使用它们将方便和快捷,而不用再单独创建和配置ViewAction对象。可以指定这样的操作:

  • ViewActions.click():点击视图控件。
  • ViewActions.typeText():点击某个视图控件,并输入指定字符串。
  • ViewActions.scrollTo():滚动到某个视图控件。目标视图控件必须是ScrollView的之类,且它android:visibility属性的值必须是VISIBLE。对于那些继承自AdapterView的视图控件(例如,ListView),onData()方法能够负责滚动。
  • ViewActions.pressKey():使用指定Keycode表示按下某键。
  • ViewActions.clearText():清除目标视图控件中的文本。

如果目标视图控件在ScrollView中,那么其它进程操作之前,需调用ViewActions.scrollTo()方法将它展示到屏幕中。此方法,对已经展示的视图控件没有影响。

3.5.Espresso-Intents做独立测试

Espresso-Intents可以验证和模拟应用程序发出的意图,通过拦截发出的意图并替换内容,然后传给被测试组件,从而单独测试应用程序、Activity或Service。使用Espresso-Intents测试之前,需要添加下面配置到工程的build.gradle文件中:

dependencies {
  // Other dependencies ...
  androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.2'
}

为了测试意图,需要创建IntentsTestRule类的一个实例,它的作用与ActivityTestRule类很相似。在每次测试之前,它用来初始化Espresso-Intents,终止当前Activity,并且在每次测试之后释放Espresso-Intents。下面代码片段展示的测试类,提供了对某显式意图的简单测试。

@Large
@RunWith(AndroidJUnit4.class)
public class SimpleIntentTest {

    private static final String MESSAGE = "This is a test";
    private static final String PACKAGE_NAME = "com.example.myfirstapp";

    /* Instantiate an IntentsTestRule object. */
    @Rule
    public IntentsTestRule≶MainActivity> mIntentsRule =
      new IntentsTestRule≶>(MainActivity.class);

    @Test
    public void verifyMessageSentToMessageActivity() {

        // Types a message into a EditText element.
        onView(withId(R.id.edit_message))
                .perform(typeText(MESSAGE), closeSoftKeyboard());

        // Clicks a button to send the message to another
        // activity through an explicit intent.
        onView(withId(R.id.send_message)).perform(click());

        // Verifies that the DisplayMessageActivity received an intent
        // with the correct package name and message.
        intended(allOf(
                hasComponent(hasShortClassName(".DisplayMessageActivity")),
                toPackage(PACKAGE_NAME),
                hasExtra(MainActivity.EXTRA_MESSAGE, MESSAGE)));

    }
}

有关Espresso-Intents的更多信息,请参阅安卓测试支持库官网上的文档,也可以下载IntentsBasicSampleIntentsAdvancedSample代码样例。

3.6.Espresso-Web测试WebViews

Espresso-Web可以测试Activity中包含的WebView组件,使用WebDriver API来检查和控制WebView的行为。使用Espresso-Web测试之前,需要添加下面配置到工程的build.gradle文件中:

dependencies {
  // Other dependencies ...
  androidTestCompile 'com.android.support.test.espresso:espresso-web:2.2.2'
}

当使用Espresso-Web创建测试时,需要在实例化ActivityTestRule对象测试Activity的同时,启用WebView的JavaScript。在测试中,可以选择WebView中展示的HTML元素和模拟用户交互,例如将文本输入文本框,然后点击按钮。操作完成之后,可以验证网页上的结果与期望的结果匹配。

下面的代码片段中,描述如何测试Activity中id为‘webview’的WebView组件。typeTextInInput_clickButton_SubmitsForm()方法描述了,选择网页上某个<input>元素,输入一些文本,然后检查文本是否出现在另一个元素中。

@LargeTest
@RunWith(AndroidJUnit4.class)
public class WebViewActivityTest {

    private static final String MACCHIATO = "Macchiato";
    private static final String DOPPIO = "Doppio";

    @Rule
    public ActivityTestRule mActivityRule =
        new ActivityTestRule(WebViewActivity.class,
            false /* Initial touch mode */, false /*  launch activity */) {

        @Override
        protected void afterActivityLaunched() {
            // Enable JavaScript.
            onWebView().forceJavascriptEnabled();
        }
    }

    @Test
    public void typeTextInInput_clickButton_SubmitsForm() {
       // Lazily launch the Activity with a custom start Intent per test
       mActivityRule.launchActivity(withWebFormIntent());

       // Selects the WebView in your layout.
       // If you have multiple WebViews you can also use a
       // matcher to select a given WebView, onWebView(withId(R.id.web_view)).
       onWebView()
           // Find the input element by ID
           .withElement(findElement(Locator.ID, "text_input"))
           // Clear previous input
           .perform(clearElement())
           // Enter text into the input element
           .perform(DriverAtoms.webKeys(MACCHIATO))
           // Find the submit button
           .withElement(findElement(Locator.ID, "submitBtn"))
           // Simulate a click via JavaScript
           .perform(webClick())
           // Find the response element by ID
           .withElement(findElement(Locator.ID, "response"))
           // Verify that the response page contains the entered text
           .check(webMatches(getText(), containsString(MACCHIATO)));
    }
}

有关Espresso-Web的更多信息,请参阅安卓测试支持库官网上的文档,也可以下载Espresso Web code sample中的上述代码片段。

3.7.验证结果

调用ViewInteraction.check()或者DataInteraction.check()方法来断言,UI中的视图控件与某些期望的状态相匹配。它们都必须传入一个ViewAssertion对象作为参数,如果断言失败,Espresso抛出AssertionFailedError异常。

ViewAssertions类为特定的常见断言提供一系列帮助方法,可以使用的包括:

  • doesNotExist:断言当前的视图层次结构中,没有视图控件匹配指定的标准。
  • matches:断言指定的视图控件存在于当前视图层次结构中,且它的状态匹配某些指定的Hamcrest匹配器。
  • selectedDescendentsMatch:断言父视图控件中的某些指定子视图控件存在,且它们的状态匹配某些指定的Hamcrest匹配器。

下面的代码片段描述如何检查UI中展示的文本,与之前输入EditText控件的文本一致。

public void testChangeText_sameActivity() {
    // Type text and then press the button.
    ...

    // Check that the text was changed.
    onView(withId(R.id.textToBeChanged))
            .check(matches(withText(STRING_TO_BE_TYPED)));
}

4.跨多个应用程序的UI测试


用户界面(UI)测试涉及跨多个应用程序的用户交互,当用户系列操作需通过其它应用程序或系统UI时,验证应用程序行为的正确性。例如这样的场景,一个短信应用程序可以让用户输入文本消息,然后启动安卓通讯录,以便选择消息的收件人,最后将控制权交还给原始应用程序,从而提交信息。

下面讲述,如何使用安卓测试支持库提供的UI Automator测试框架,编写这样的UI测试。它的APIs可以与设备上可见元素交互,不用关心当前的Activity应是哪个。测试可以使用便捷的描述符查找UI组件,例如那个组件显示的文本或它的内容描述。UI Automator测试框架可以运行于Android 4.3(API 18)及以上版本的设备,是基于设备的API,且与AndroidJUnitRunner测试运行器一起使用。

4.1.设置UI Automator

使用UI Automator构建UI测试之前,确保配置好测试源代码的位置和项目依赖。在安卓app模块的build.gradle文件中,必须设置对UI Automator库的依赖引用:

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

为了优化UI Automator测试,首先应该检查目标应用程序的UI组件,并确保它们可被访问,具体优化细节将在后面描述。

  • 检查设备上的UI

    设计测试之前,检查UI组件在设备上是否可见,来确保测试时能访问这些组件,从而判断它们是否有可见的文本标签或android:contentDescription属性的值,或以上两者。uiautomatorviewer工具提供方便的可视化界面来检查布局的层次结构和查看设备前景中可见的UI组件的属性。通过这些信息,可使用UI Automator创建更细粒度的测试。例如,可创建匹配特定可见属性的UI选择器。

    启动uiautomatorviewer工具的步骤:

    • 在实际设备上启动目标应用程序。

    • 将设备与开发机器相连接。

    • 打开命令窗口,然后前往<android-sdk>/tools/目录。

    • 使用这个命令运行工具:

      $ uiautomatorviewer
      

    查看应用程序的UI属性:

    • uiautomatorviewer界面,点击Device Screenshot按钮。
    • 通过左面板中显示的快照可以看到由工具识别的UI组件,同时在右下面板中列出属性,在右上面板中展示布局层次结构。
    • 也可以点击Toggle NAF Nodes按钮来查看UI Automator不能访问的UI组件,但能提供的有效信息很有限。

    要了解安卓提供的常见UI组件类型,请参阅用户界面

  • 确保Activity可被访问

    UI Automator测试框架更适合已实现安卓访问功能的应用程序。若使用View类型的UI元素,或SDK、支持库提供的View的子类,不需要自己实现访问功能,因为这些类已经完成了。但有些应用程序使用自定义UI元素提供更丰富的用户体验,这些元素不会自动提供访问功能。如果应用程序包含View子类(不来自SDK或支持库)的实例,通过完成以下步骤,确保给它们添加访问功能:

    • 创建一个继承自ExploreByTouchHelper的具体类。
    • 通过调用setAccessibilityDelegate()方法,将上面类的实例与指定自定义UI元素关联。

    给自定义视图元素添加访问功能的更多指南,请参阅构建可访问的自定义视图控件。想了解更多关于安卓可访问性的一般最佳实践,请参阅让应用程序更易访问

4.2.创建UI Automator测试类

UI Automator测试类应该写成JUnit 4测试类。要了解更多关于创建JUnit 4测试类和使用JUnit 4断言及注解,请参阅创建设备单元测试类。测试类定义之前,添加 @RunWith(AndroidJUnit4.class)注解,还需要指定安卓测试支持库提供的AndroidJUnitRunner类作为默认测试运行器。在UI Automator测试类中实现下面的开发步骤:

  • 通过调用getInstance()方法并传入Instrumentation对象作为参数,获取UiDevice对象来访问想要测试的设备。
  • 通过调用findObject()方法,获取UiObject对象来访问设备上显示的UI组件(例如,前景中当前视图控件)。
  • 通过调用UiObject的方法,对UI组件模拟特定用户交互;例如,调用performMultiPointerGesture()方法模拟多点触摸手势和setText()方法编辑文本字段。必要时可以反复调用步骤2和3中的APIs,来测试更复杂的用户交互,涉及多个UI组件或用户操作序列。
  • 这些用户交互执行后,检查UI的反应是否是预期的状态或行为。

在后面的几节中将包含更详细的介绍。

4.3.运行UI Automator测试

可以从Android Studio或命令行运行UI Automator测试,确认项目已指定AndroidJUnitRunner作为默认设备运行器。

5.UI Automator使用详解


5.1.访问UI组件

UiDevice对象是访问和操作设备状态的主要途径。测试时,可以调用UiDevice的方法来检查各种属性的状态,例如当前横竖屏或显示尺寸。也可以使用UiDevice对象执行设备级别的操作,例如强制设备横竖屏、按方向物理键和按主页及菜单键。

从设备的主屏幕开始(或设备上选定的其它开始位置)测试是好的做法,可以调用UI Automator API提供方法来选择特定UI元素,并与之交互。下面的代码片段展示,测试中如何获取UiDevice实例和模拟按主页键:

import org.junit.Before;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.By;
import android.support.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 mDevice;

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

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

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

        // Launch the app
        Context context = InstrumentationRegistry.getContext();
        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
        mDevice.wait(Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)),
                LAUNCH_TIMEOUT);
    }
}

在这个例子中,@SdkSuppress(minSdkVersion = 18)注解有助于确保测试,如UI Automator框架要求的,将只运行在Android 4.3(API 18)及以上版本设备。

使用findObject()方法可获取UiObject,它代表符合给定选择标准的视图控件。若需要,在为同一应用程序创建的其它测试中,可以重复使用该UiObject实例。需注意的是,每次使用UiObject实例来点击UI元素或查询属性,UI Automator测试框架都会搜索当前显示进行匹配。下面的代码片段展示,测试中如何构建代表取消按钮和同意按钮的UiObject实例。

UiObject cancelButton = mDevice.findObject(new UiSelector()
        .text("Cancel")
        .className("android.widget.Button"));
UiObject okButton = mDevice.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();
}
5.2.指定匹配器

如果想访问应用程序中指定UI组件,使用UiSelector类。它代表在当前UI显示中,查询指定元素。如果发现匹配的元素不止一个,在布局层次结构中第一个匹配的元素作为目标UiObject返回。当构建UiSelector时,可以将多个属性链接在一起,来增强搜索。如果发现没有匹配的UI元素,抛出UiAutomatorObjectNotFoundException异常。

也可以使用childSelector()方法嵌套多个UiSelector实例。例如,下面的代码示例展示,测试时如何在当前UI显示中指定查找第一个ListView的搜索,接着在ListView中搜索文本属性是“Apps”的UI元素。

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

作为最佳实践,若需要指定个选择器,应该使用资源ID(如果已经定义了的话),而不是文本元素或内容描述符。并不是所有元素都有文本元素,例如,Toolbar中的图标。文本选择器不可靠,若对UI做微小改变,可能导致测试失败;它也不支持跨不同语言,可能无法匹配翻译后的字符串。在选择器的条件中指定对象状态是有用的,例如,选择了一组已经检查过的元素(不需要再次检查),调用checked()方法并将参数设置为True。

5.3.执行交互

一旦测试已经获取UiObject对象,可以调用其中的方法来对自己代表的UI组件执行用户交互,例如指定这样的操作:

  • click():点击UI元素可见范围的中心。
  • dragTo():拖动这个对象到任意位置。
  • setText():在清除可编辑控件的内容之后,设置新的文本。相反的,clearTextField()方法清除可编辑控件内的现有文本。
  • swipeUp():对UiObject执行上滑操作。相似的,swipeDown()swipeLeft()swipeRight()方法会执行对应操作。

UI Automator测试框架不需要使用Shell命令发送Intent或启动Activity,而是通过getContext()方法获取Context对象。下面的代码片段展示,测试中如何使用Intent启动被测试的应用程序。当只对测试计算器应用程序感兴趣,而不关心启动器时,这个方法才有用。

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);
    mDevice.wait(Until.hasObject(By.pkg(CALC_PACKAGE).depth(0)), TIMEOUT);
}
5.4.对集合执行交互

如果想要对元素的集合(例如,音乐专辑中的歌曲或收件箱中的邮件)模拟用户交互,可以使用UiCollection类。为了创建UiCollection对象,指定一个UiSelector来搜索UI容器或其它子UI元素的包装器,例如包含子UI元素的布局视图控件。下面的代码片段展示,测试中如何构造UiCollection来代表显示在FrameLayout中的视频专辑:

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();
5.5.对可滑动的视图执行交互

使用UiScrollable类在显示器上模拟竖向或横向滑动。当UI元素位于屏幕之外,且需要滑动使其进入视图控件时,这个技巧很有帮助。下面的代码片段展示,如何模拟设置菜单的下滑和点击About tablet的选项:

UiScrollable settingsItem = new UiScrollable(new UiSelector()
        .className("android.widget.ListView"));
UiObject about = settingsItem.getChildByText(new UiSelector()
        .className("android.widget.LinearLayout"), "About tablet");
about.click();
5.6.验证结果

InstrumentationTestCase继承自TestCase,所以可使用标准的JUnit断言方法,来测试应用程序中的UI组件是否返回预期的结果。下面的代码片段展示,测试中如何能够定位计算器应用程序中的几个按钮,按顺序点击它们,然后验证是否显示正确的结果。

private static final String CALC_PACKAGE = "com.myexample.calc";

public void testTwoPlusThreeEqualsFive() {
    // Enter an equation: 2 + 3 = ?
    mDevice.findObject(new UiSelector()
            .packageName(CALC_PACKAGE).resourceId("two")).click();
    mDevice.findObject(new UiSelector()
            .packageName(CALC_PACKAGE).resourceId("plus")).click();
    mDevice.findObject(new UiSelector()
            .packageName(CALC_PACKAGE).resourceId("three")).click();
    mDevice.findObject(new UiSelector()
            .packageName(CALC_PACKAGE).resourceId("equals")).click();

    // Verify the result = 5
    UiObject result = mDevice.findObject(By.res(CALC_PACKAGE, "result"));
    assertEquals("5", result.getText());
}

6.总结


UI测试由于是模拟用户操作,所以属于黑盒测试范畴。一般都是请专业测试人员,设计测试用例来覆盖各种情况,并手动操作设备来完成用例。由于手动操作属于重复、无技术含量的体力工作,可以通过代码来实现自动化,解放测试人员的劳动力,让他们专注于设计更全更好的测试用例。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,175评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,674评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,151评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,597评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,505评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,969评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,455评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,118评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,227评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,213评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,214评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,928评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,512评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,616评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,848评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,228评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,772评论 2 339

推荐阅读更多精彩内容