アプリのアクティビティをテストする

アクティビティは、アプリ内ですべてのユーザー操作のコンテナとして機能します。そのため、次のようなデバイスレベルのイベント中に、アプリのアクティビティがどのように動作するかをテストすることが重要です。

  • デバイスの電話アプリなど、別のアプリがアプリのアクティビティに割り込む。
  • システムによってアクティビティが破棄され、再作成される。
  • ユーザーが、ピクチャー イン ピクチャー(PIP)やマルチウィンドウなどの新しいウィンドウ環境にアクティビティを配置する。

特に、アクティビティのライフサイクルについてに記載されているイベントに応じて、アクティビティが正しく動作するのを確認することが重要です。

このガイドでは、ライフサイクルの各段階を経てアプリのアクティビティが遷移する際に、データの整合性と優れたユーザー エクスペリエンスを維持するアプリの機能を評価する方法について説明します。

アクティビティの状態を遷移させる

アプリのアクティビティをテストする際に重要なことの 1 つは、アプリのアクティビティを特定の状態に置くことに関連します。テストの「特定の状態」を定義するには、AndroidX Test ライブラリの一部である ActivityScenario のインスタンスを使用します。このクラスを使用すると、このページの冒頭で説明したデバイスレベルのイベントをシミュレートする状態にアクティビティを配置できます。

ActivityScenario は、ローカル ユニットのテストとデバイス上での統合テストで同様に使用できるクロス プラットフォーム API です。実際のデバイスまたは仮想デバイスで、ActivityScenario によってスレッドの安全性が実現し、テストのインストルメンテーション スレッドとテスト中のアクティビティを実行するスレッド間でイベントの同期が行われます。この API は、テスト対象のアクティビティが破棄または作成された場合に、どのように動作するかを評価するのにも非常に適しています。

本セクションでは、この API に関連する最も一般的なユースケースを示します。

アクティビティを作成する

テスト対象のアクティビティを作成するには、次のスニペットに示すコードを追加します。

    @RunWith(AndroidJUnit4::class)
    class MyTestSuite {
        @Test fun testEvent() {
            val scenario = launchActivity<MyActivity>()
        }
    }
    

アクティビティを作成すると、アクティビティは ActivityScenario によって RESUMED の状態に移行します。この状態はアクティビティが実行中で、ユーザーに表示されていることを示します。この状態では、Espresso UI テストを使用してアクティビティの View 要素を自由に操作できます。

または、ActivityScenarioRule を使用して各テストの前に ActivityScenario.launch を自動的に呼び出したり、テストのティアダウンで ActivityScenario.close を自動的に呼び出したりできます。以下の例では、ルールを定義して、そのルールからシナリオのインスタンスを取得する方法を示しています。

    @RunWith(AndroidJUnit4::class)
    class MyTestSuite {
        @get:Rule var activityScenarioRule = activityScenarioRule<MyActivity>()

        @Test fun testEvent() {
            val scenario = activityScenarioRule.scenario
        }
    }
    

アクティビティを新しい状態に遷移させる

アクティビティを CREATED または STARTED などの別の状態に遷移させるには、moveToState() を呼び出します。この操作は、別のアプリまたはシステム操作の割り込みによって、アクティビティが停止または一時停止される場合の状況をシミュレートします。

moveToState() の使用例を次のコード スニペットに示します。

    @RunWith(AndroidJUnit4::class)
    class MyTestSuite {
        @Test fun testEvent() {
            val scenario = launchActivity<MyActivity>()
            scenario.moveToState(State.CREATED)
        }
    }
    

現在のアクティビティの状態を確認する

テスト対象のアクティビティの現在の状態を確認するには、ActivityScenario オブジェクト内の state フィールドの値を取得します。次のコード スニペットで示すように、アクティビティが終了した場合もしくは別のアクティビティにリダイレクトされた場合に、テスト対象のアクティビティの状態を確認する際に便利です。

    @RunWith(AndroidJUnit4::class)
    class MyTestSuite {
        @Test fun testEvent() {
            val scenario = launchActivity<MyActivity>()
            scenario.onActivity { activity ->
              startActivity(Intent(activity, MyOtherActivity::class.java))
            }

            val originalActivityState = scenario.state
        }
    }
    

アクティビティを再作成する

デバイスのリソースが不足していると、システムがアクティビティを破棄することがあり、その場合、ユーザーがアプリに戻った際にアプリがアクティビティを再作成する必要が生じます。これらの条件をシミュレートするには、recreate() を呼び出します。

    @RunWith(AndroidJUnit4::class)
    class MyTestSuite {
        @Test fun testEvent() {
            val scenario = launchActivity<MyActivity>()
            scenario.recreate()
        }
    }
    

ActivityScenario クラスは、アクティビティの保存済みインスタンスの状態と、@NonConfigurationInstance アノテーションが付けられたオブジェクトを保持します。これらのオブジェクトは、テスト対象のアクティビティの新しいインスタンスに読み込まれます。

アクティビティの検索結果を取得する

完成したアクティビティに関連付けられた結果のコードまたはデータを取得するには、次のコード スニペットに示すように、ActivityScenario オブジェクト内の result フィールドの値を取得します。

    @RunWith(AndroidJUnit4::class)
    class MyTestSuite {
        @Test fun testResult() {
            val scenario = launchActivity<MyActivity>()
            onView(withId(R.id.finish_button)).perform(click())

            // Activity under test is now finished.

            val resultCode = scenario.result.resultCode
            val resultData = scenario.result.resultData
        }
    }
    

アクティビティでアクションをトリガーする

ActivityScenario 内のすべてのメソッドはブロッキング呼び出しであるため、API ではインストルメンテーション スレッドで実行する必要があります。

テスト対象のアクティビティでアクションをトリガーするには、Espresso ビュー マッチャーを使用してビュー内の要素を操作します。

    @RunWith(AndroidJUnit4::class)
    class MyTestSuite {
        @Test fun testEvent() {
            val scenario = launchActivity<MyActivity>()
            onView(withId(R.id.refresh)).perform(click())
        }
    }
    

ただし、アクティビティ自体でメソッドを呼び出す必要がある場合は、ActivityAction を実装することで安全に実行できます。

    @RunWith(AndroidJUnit4::class)
    class MyTestSuite {
        @Test fun testEvent() {
            val scenario = launchActivity<MyActivity>()
            scenario.onActivity { activity ->
              activity.handleSwipeToRefresh()
            }
        }
    }
    

Android テストでのスレッドの動作について詳しくは、テストのスレッドについてをご覧ください。