Android 11 デベロッパー プレビュー 2 が公開されました。ぜひお試しのうえ、フィードバックをお寄せください

Espresso-Intents

Espresso-Intents は Espresso の拡張機能です。これを使用すると、テスト対象のアプリから送信されるインテントの検証やスタブ化を行えます。Mockito に似ていますが、Android のインテントを対象としています。

テスト対象アプリが他のアプリやプラットフォームに機能を委任する場合、Espresso-Intents を使用することにより、他のアプリやプラットフォームが正しく機能する前提で、対象アプリのロジックにテストの焦点を絞れます。Espresso-Intents を使用すれば、送信されるインテントのマッチングや検証を行えるうえ、実際のインテント レスポンスの代わりにスタブ レスポンスを使うこともできます。

プロジェクトへの 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 クラスの拡張機能であり、UI の機能テストで Espresso-Intents の API を簡単に使えるようにするためのものです。IntentsTestRule により、テストの前に Espresso-Intents が初期化され、テストの実施後に Espresso-Intents がリリースされます。対象とするテストには、@Test アノテーションを付けます。

次のコード スニペットは、IntentsTestRule の例です。

Kotlin

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

Java

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

マッチング

Espresso-Intents には、Hamcrest マッチャーで定義したマッチング基準に基づいて、送信されるインテントをインターセプトする機能が用意されています。Hamcrest により次のことが可能になります。

  • 既存のインテント マッチャーの使用: 最も簡単なオプションで、ほとんどのケースに推奨されます。
  • 独自のインテント マッチャーの実装: 最も自由度の高いオプションです。詳細については、Hamcrest チュートリアルの「カスタム マッチャーの作成」のセクションをご覧ください。

Espresso-Intents では、インテントの検証とスタブ化のために、それぞれ intended() メソッドと intending() メソッドが用意されています。どちらも、Hamcrest Matcher<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");
    

インテントの検証

Espresso-Intents では、テスト対象のアプリからアクティビティを起動しようとするすべてのインテントが記録されます。Mockito.verify() メソッドに似た intended() メソッドを使用すると、特定のインテントが確認されたことに対してアサーションを行うことができます。ただし、Espresso-Intents では明示的に構成しない限り、インテントへのレスポンスはスタブ化されません。

次のコード スニペットは、外部の「電話」アクティビティを起動する発信インテントについて、検証はするもののレスポンスのスタブ化は行わないテストの例です。

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

スタブ化

Mockito.when() に似た intending() メソッドを使用することにより、startActivityForResult() で起動するアクティビティに対するスタブ レスポンスを用意できます。これは、特に外部のアクティビティに対して有効です。なぜなら、外部のアクティビティに対しては、ユーザー インターフェースを操作したり、テスト対象アクティビティに返される ActivityResult を制御したりできないためです。

次のコード スニペットは、activityResult_DisplaysContactsPhoneNumber() のサンプルテストを実装するものです。ユーザーがテスト対象アプリで「連絡先」アクティビティを起動したときに連絡先の電話番号が表示されることを、次のようにして確認します。

  1. 特定のアクティビティが起動された際に返す結果を準備します。サンプルテストでは、「連絡先」に送信されるすべてのインテントをインターセプトし、結果コード RESULT_OK を使用して、有効な ActivityResult でレスポンスをスタブ化します。

    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 に指示します。

    Kotlin

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

    Java

        intending(toPackage("com.android.contacts")).respondWith(result);
        
  3. アクティビティの起動に使用するアクションによって期待するスタブ結果が生成されることを確認します。このサンプルテストでは、「連絡先アクティビティ」が起動されたときに、電話番号「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)));
    }
    

参考情報

Espresso-Intents を使用した Android のテストに関するその他の情報については、次のリソースをご覧ください。

サンプル

  • IntentsBasicSample: intended()intending() の基本的な使用方法。
  • IntentsAdvancedSample: カメラを使用してビットマップを取得するユーザーの操作をシミュレートします。