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 は、@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 マッチャーで定義したマッチング基準に基づいて、送信されるインテントをインターセプトする機能が用意されています。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: カメラを使用してビットマップを取得するユーザーの操作をシミュレートします。