Android测试官方教程翻译(四)--单APP的UI测试

单APP的UI测试

测试一个APP内的用户交互,有助于确保当用户与你的APP交互时,不会遇到未预料到的结果或者有一个糟糕的体验。如果你需要验证你的APP的UI功能正确,你应当养成创建用户界面(UI)测试的习惯。

Android Testing Support Library(安卓测试支持库) 提供的 Espresso 测试框架,提供了编写UI测试以模拟单个APP内的用户交互的API, Espresso测试能够运行在运行Android 2.3.3 (API level 10) 或者更高版本的设备上,使用Espresso的一个关键好处是,对于在测试APP的UI,它提供了测试动作的自动化同步。Espresso侦测何时主线程空闲,所以它可以在合适的时机运行测试指令。这让你从必须添加timing workarounds,比如Thread.sleep()中解脱出来。

Espresso测试框架是基于设备的API,与 AndroidJUnitRunner 协同工作。

设置Espresso

在用Espresso构建你的UI测试,确保配置你的源代码路径以及工程依赖,如 Android测试官方教程翻译(一)–Android 测试介绍 所述。

在你APP模块的build.gradle文件中,你必须设置Espresso库的依赖引用。

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

关闭测试设备,测试设备系统动画保持开启可能引起异常结果或者导致测试失败。打开设备开发者选项,关闭以下所有选项以从设置关闭动画。

  • Window animation scale
  • Transition animation scale
  • Animator duration scale

除了核心API提供的功能,如果你想设置你的工程使用Espresso特性,戳这儿

创建一个Espresso测试类

依照如下编程模型创建一个JAVA类,以创建一个 Espresso 测试。
1. 通过调用 onView() 方法或者为 AdapterView控制设计的 onData()方法,在一个Activity中找到你想测试的UI组件(比如一个APP中的登陆按键)。
2. 通过调用 ViewInteraction.perform() 或者 DataInteraction.perform()方法,并且传入用户动作参数(比如点击登陆按键),模拟特定用户交互来执行在UI组件上。为了顺序排列同一UI组件上的多个动作,在你的方法参数中用逗号分隔符将动作串成表。
3. 如有需要重复以上动作,来模拟目标APP中跨多个Activity的用户操作流。
4. 在交互执行之后,使用 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

同ActivityTestRule使用Espresso

如下片段描述了如何依照JUnit 4风格创建一个新的 Espresso测试,并且使用 ActivityTestRule来减少你需要编写的样板代码的工作量。在每一个具有@Test注释的测试方法和任何有@Before注释的方法之前,测试框架启动测试Activity。在测试结束,并且所有具有 @After注释的方法运行之后,框架完全终止Activity。

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)));
    }
}

获取UI组件

在Espresso能够与测试中的APP交互之前,你必须首先明确UI组件或者view,对于明确APP中的View或者Adapter,Espresso支持 Hamcrest 的使用。

为了找到view,调用 onView()方法并且传入明确目标View的view matcher。 onView()方法返回一个允许你的测试与view交互的 ViewInteraction 对象。然而对于 RecyclerView布局中的view,调用onView()可能并不奏效。这种情况下,参照下文Locating a view in an AdapterView 的说明。

注意: onView() 方法并不检查你指定的view是否有效。相反,Espresso使用matcher,仅仅搜寻当前view层级,如果未发现匹配的,这个方法抛出 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.
    ...
}
指定view matcher

通过如下方法,你可以明确一个view matcher

  • 调用 ViewMatchers 类中的方法。比如,通过查找所展示文本找到一个view,你可以这样调用方法。
onView(withText("Sign-in"));

相似的,你可以调用 withId()方法,提供view的资源ID,如下示例所示。

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

Android的资源ID并不确保是唯一的。如果你的测试尝试匹配一个被多个view使用的ID,Espresso抛出 AmbiguousViewMatcherException异常。

  • 使用Hamcrest 的Matchers类,你可以使用allOf() 方法组合多个 matchers,比如 containsString()和instanceOf()。这种方法让你能够更苛刻的过滤匹配结果,如下示例所示。
onView(allOf(withId(R.id.button_signin), withText("Sign-in")));

过滤并不符合matcher的view,你可以使用not关键词,如下示例所示。

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

在你的测试中使用这些方法,需要导入org.hamcrest.Matchers包。学习更多Hamcrest matching,戳这儿

为了提高Espresso测试的性能,明确需要查找到目标view的最小匹配信息。

查找AdapterView中的view (Locating a view in an AdapterView)

在一个 AdapterView 中,子view是在运行时动态加载的,如果你测试的目标view是在一个AdapterView中(比如 ListView, GridView, o或者Spinner), onView()方法可能并不奏效,因为在当前view层级中,可能仅仅view的一个子集被加载。

相反,调用 onData()方法,以包含一个 DataInteraction 对象,来获取目标view。Espresso处理加载目标view到当前view层级。Espresso也会滑动到目标view元素,并让其获取焦点。

注意:onData()方法并不检测你指定的条目和一个view相符合。Espresso 仅仅搜寻当前view层级,如果没有发现匹配的,该方法抛出  NoMatchingViewException异常。

如下代码片向你展示你如何和 Hamcrest matching 一起使用 onData()方法,来搜寻一个list中的包含给定文本的指定行。在这个示例中,LongListActivity包含了一个通过 SimpleAdapter暴露的字符串列表。

onData(allOf(is(instanceOf(Map.class)),
        hasEntry(equalTo(LongListActivity.ROW_TEXT), is("test input")));

执行动作

调用 ViewInteraction.perform() 或者 DataInteraction.perform() 方法来模拟UI组件上的用户交互。你必须传入一个或者多个 ViewAction 对象作为参数。 Espresso根据给定顺序,按序列引发动作,在主线程执行动作。
ViewActions类为通常的动作提供了一系列帮助方法。替代创建和配置个人的ViewAction对象,你可以使用这些方法作为便捷操作。你可以如此明确动作。

  • ViewActions.click():点击view。
  • ViewActions.typeText():点击view并且输入指定的字符串。
  • ViewActions.scrollTo():滑动到view。目标view必须是 ScrollView的子类,并且它的 android:visibility属性必须是visibility。对于继承自 AdapterView的view(比如listview), onData()方法为你处理滑动。
  • ViewActions.pressKey():使用制定的keycode执行按键按压。
  • ViewActions.clearText():清楚目标view的文本。
    如果目标view是在一个scrollview的内部,在其他动作执行之前,首先执行 ViewActions.scrollTo() 动作将view显示在屏幕上。如果view已经显示, ViewActions.scrollTo() 动作没有效果。

使用 Espresso Intents隔离测试你的activities

使用Espresso的intents,你可以通过阻断向外intent、stub结果,发送回测试中的组件,隔离的测试一个APP、activity或者service。

开始使用Espresso Intents测试,你需要添加以下行到你的build.gradle中。

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

为了测试intent,你需要创建一个 IntentsTestRule 类的实例,它和 IntentsTestRule 类非常相似。在任何测试之前, IntentsTestRule初始化Espresso Intents ,终止host activity,并且在每一个测试之后释放 Espresso Intents 。

以下代码片展示的测试类,提供了一个明确intent的简单测试。

@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的信息,查阅 Espresso Intents ,你也可以下载 IntentsBasicSample IntentsAdvancedSample 代码示例。

使用 Espresso Web测试WebViews

Espresso Web使你能够测试包含在一个activity中的webview,它使用 WebDriver API 来检测和和控制webview的行为。

开始使用Espresso Web进行测试,你需要添加以下行到你的APP的build.gradle文件中

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

当使用 Espresso Web创建一个测试,当你实例化 ActivityTestRule对象来测试activity时,你需要使能webview的javascript。在测试中,你可以选择显示在webview中的HTML元素并且模拟用户交互,比如输入文本到一个文本框中,然后点击一个按键。当动作执行完,你可以验证web页显示的结果和你的预期结果一致。

以下代码片,这个类测试了测试Activity的id值为“webview”的 WebView。verifyValidInputYieldsSuccesfulSubmission()测试选择了web页的 元素,输入一些文本,检查另外一个元素中显示的文本。

@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 documentation on the Android Testing Support Library site ,你也可以下载 Espresso Web code sample 的代码。

验证结果

调用 ViewInteraction.check() 或者 DataInteraction.check() 来断言UI中的view匹配预期的状态。你必须传入一个 ViewAssertion对象作为参数。如果断言失败,Espresso抛出 AssertionFailedError异常。

ViewAssertions类为通用的断言提供了一系列帮助方法。你可用的方法包括:

  • doesNotExist:断言在当前view层级中没有view匹配指定的标准。
  • matches:断言在当前view层级中指定view存在,并且它的状态匹配一些给定的Hamcrest matcher
  • selectedDescendentsMatch:断言父view的一个指定子view存在,并且它的状态匹配一些给定的Hamcrest matcher。

以下代码片展示了你可能如何检查UI中显示的text和之前在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)));
}

在设备或者模拟器上运行Espresso测试

你可以从AndroidStudio或者命令行运行Espresso测试。确保在你的工程中指定 AndroidJUnitRunner作为默认设备运行器。

运行 Espresso测试,参照第一篇译文中描述的运行设备测试步骤。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值