Espresso-Intents

Espresso-Intents 是 Espresso 的扩展,支持对被测应用发出的 intent 进行验证和打桩。它与 Mockito 类似,但适用于 Android Intent。

如果您的应用将功能委托给其他应用或平台,您可以使用 Espresso-Intents 来专注于自己应用的逻辑,同时假定其他应用或平台能够正常运行。借助 Espresso-Intents,您可以匹配和验证您的传出 intent,甚至可以提供桩响应来代替实际的 intent 响应。

在项目中添加 Espresso-Intents

在应用的 app/build.gradle 文件中,在 dependencies 内添加以下代码行:

androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.0'
    

Espresso-Intents 只与 Espresso 2.1 及更高版本以及 Android 测试库 0.3 及更高版本兼容,因此请务必也更新以下代码行:

androidTestImplementation 'androidx.test:runner:1.1.0'
    androidTestImplementation 'androidx.test:rules:1.1.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
    

编写测试规则

在编写 Espresso-Intents 测试之前,先设置 IntentsTestRule。这是 ActivityTestRule 类的扩展,可让您在功能界面测试中轻松使用 Espresso-Intents API。IntentsTestRule 会在带有 @Test 注解的每个测试运行前初始化 Espresso-Intents,并在每个测试运行后释放 Espresso-Intents。

以下代码段是 IntentsTestRule 的一个示例:

Kotlin

    @get:Rule
    val intentsTestRule = IntentsTestRule(MyActivity::class.java)
    

Java

    @Rule
    public IntentsTestRule<MyActivity> intentsTestRule =
        new IntentsTestRule<>(MyActivity.class);
    

匹配

Espresso-Intents 能够根据使用 Hamcrest 匹配器定义的某些匹配条件来拦截传出 intent。Hamcrest 可让您:

  • 使用现有的 intent 匹配器:这是最简单的选项,应该几乎总是首选。
  • 实现您自己的 intent 匹配器:这是最灵活的选项。如需了解详情,请参阅 Hamcrest 教程中标题为“编写自定义匹配器”的部分。

Espresso-Intents 分别为 intent 验证和打桩提供了 intended()intending() 方法。这两者都以 Hamcrest Matcher<Intent> 对象作为参数。

以下代码段展示了 intent 验证,使用的是现有 intent 匹配器,匹配用来启动浏览器的传出 intent:

Kotlin

    assertThat(intent).hasAction(Intent.ACTION_VIEW)
    assertThat(intent).categories().containsExactly(Intent.CATEGORY_BROWSABLE)
    assertThat(intent).hasData(Uri.parse("www.google.com"))
    assertThat(intent).extras().containsKey("key1")
    assertThat(intent).extras().string("key1").isEqualTo("value1")
    assertThat(intent).extras().containsKey("key2")
    assertThat(intent).extras().string("key2").isEqualTo("value2")
    

Java

    assertThat(intent).hasAction(Intent.ACTION_VIEW);
    assertThat(intent).categories().containsExactly(Intent.CATEGORY_BROWSABLE);
    assertThat(intent).hasData(Uri.parse("www.google.com"));
    assertThat(intent).extras().containsKey("key1");
    assertThat(intent).extras().string("key1").isEqualTo("value1");
    assertThat(intent).extras().containsKey("key2");
    assertThat(intent).extras().string("key2").isEqualTo("value2");
    

验证 intent

Espresso-Intents 会记录尝试从被测应用启动 Activity 的所有 intent。使用 intended() 方法(类似于 Mockito.verify()),您可以断言已看到给定的 intent。不过,Espresso-Intents 不会对 intent 的响应打桩,除非您进行了明确配置来要求它这样做。

以下代码段是一个示例测试,该测试验证用来启动外部“phone”Activity 的传出 intent 的响应,但不对其进行打桩:

Kotlin

    @Test fun validateIntentSentToPackage() {
        // User action that results in an external "phone" activity being launched.
        user.clickOnView(system.getView(R.id.callButton))

        // Using a canned RecordedIntentMatcher to validate that an intent resolving
        // to the "phone" activity has been sent.
        intended(toPackage("com.android.phone"))
    }
    

Java

    @Test
    public void validateIntentSentToPackage() {
        // User action that results in an external "phone" activity being launched.
        user.clickOnView(system.getView(R.id.callButton));

        // Using a canned RecordedIntentMatcher to validate that an intent resolving
        // to the "phone" activity has been sent.
        intended(toPackage("com.android.phone"));
    }
    

打桩

使用 intending() 方法(类似于 Mockito.when()),您可以为使用 startActivityForResult() 启动的 Activity 提供桩响应。这对于外部 Activity 特别有用,因为您无法操纵外部 Activity 的界面,也无法控制返回给被测 Activity 的 ActivityResult

以下代码段实现了一个示例 activityResult_DisplaysContactsPhoneNumber() 测试,该测试验证当用户在被测应用中启动“contacts”Activity 时,是否会显示相应的联系人电话号码:

  1. 构建要在启动特定 Activity 时返回的结果。该示例测试会拦截发送到“contacts”的所有 intent 并使用有效的 ActivityResult 对其响应进行打桩(使用结果代码 RESULT_OK):

    Kotlin

        val resultData = Intent()
        val phoneNumber = "123-345-6789"
        resultData.putExtra("phone", phoneNumber)
        val result = Instrumentation.ActivityResult(Activity.RESULT_OK, resultData)
        

    Java

        Intent resultData = new Intent();
        String phoneNumber = "123-345-6789";
        resultData.putExtra("phone", phoneNumber);
        ActivityResult result =
            new ActivityResult(Activity.RESULT_OK, resultData);
        
  2. 指示 Espresso 提供桩结果对象来响应“contacts”intent 的所有调用:

    Kotlin

        intending(toPackage("com.android.contacts")).respondWith(result)
        

    Java

        intending(toPackage("com.android.contacts")).respondWith(result);
        
  3. 验证用于启动该 Activity 的操作是否产生了预期的桩结果。在本例中,该示例测试会检查当启动“contacts”Activity 时是否返回并显示了电话号码“123-345-6789”

    Kotlin

        onView(withId(R.id.pickButton)).perform(click())
        onView(withId(R.id.phoneNumber)).check(matches(withText(phoneNumber)))
        

    Java

        onView(withId(R.id.pickButton)).perform(click());
        onView(withId(R.id.phoneNumber)).check(matches(withText(phoneNumber)));
        

下面是完整的 activityResult_DisplaysContactsPhoneNumber() 测试:

Kotlin

    @Test fun activityResult_DisplaysContactsPhoneNumber() {
        // Build the result to return when the activity is launched.
        val resultData = Intent()
        val phoneNumber = "123-345-6789"
        resultData.putExtra("phone", phoneNumber)
        val result = Instrumentation.ActivityResult(Activity.RESULT_OK, resultData)

        // Set up result stubbing when an intent sent to "contacts" is seen.
        intending(toPackage("com.android.contacts")).respondWith(result)

        // User action that results in "contacts" activity being launched.
        // Launching activity expects phoneNumber to be returned and displayed.
        onView(withId(R.id.pickButton)).perform(click())

        // Assert that the data we set up above is shown.
        onView(withId(R.id.phoneNumber)).check(matches(withText(phoneNumber)))
    }
    

Java

    @Test
    public void activityResult_DisplaysContactsPhoneNumber() {
        // Build the result to return when the activity is launched.
        Intent resultData = new Intent();
        String phoneNumber = "123-345-6789";
        resultData.putExtra("phone", phoneNumber);
        ActivityResult result =
            new ActivityResult(Activity.RESULT_OK, resultData);

        // Set up result stubbing when an intent sent to "contacts" is seen.
        intending(toPackage("com.android.contacts")).respondWith(result);

        // User action that results in "contacts" activity being launched.
        // Launching activity expects phoneNumber to be returned and displayed.
        onView(withId(R.id.pickButton)).perform(click());

        // Assert that the data we set up above is shown.
        onView(withId(R.id.phoneNumber)).check(matches(withText(phoneNumber)));
    }
    

其他资源

如需详细了解如何在 Android 测试中使用 Espresso-Intents,请参阅以下资源。

示例