一般的なパターン

Compose アプリは、確立されたアプローチとパターンでテストできます。

単独でのテスト

ComposeTestRule を使用すると、任意のコンポーザブル(アプリケーション全体、単一の画面、小さな要素など)を表示するアクティビティを開始できます。また、コンポーザブルが正しくカプセル化され、独立して動作することを確認することもおすすめします。これにより、UI テストをより簡単かつ集中できます。

これは、単体 UI テストのみを作成するということではありません。UI テストでは、UI の大きな部分にスコープ設定することも非常に重要です。

独自のコンテンツの設定後にアクティビティとリソースにアクセスする

多くの場合、composeTestRule.setContent を使用してテスト対象のコンテンツを設定する必要があり、アクティビティ リソースにアクセスする必要もあります。たとえば、表示されるテキストが文字列リソースに一致することをアサートします。ただし、アクティビティがすでに setContent を呼び出している場合、createAndroidComposeRule() で作成したルールに対してまた呼び出すことはできません。

これを実現するための一般的なパターンは、ComponentActivity などの空のアクティビティを使用して AndroidComposeTestRule を作成することです。

class MyComposeTest {

    @get:Rule
    val composeTestRule = createAndroidComposeRule<ComponentActivity>()

    @Test
    fun myTest() {
        // Start the app
        composeTestRule.setContent {
            MyAppTheme {
                MainScreen(uiState = exampleUiState, /*...*/)
            }
        }
        val continueLabel = composeTestRule.activity.getString(R.string.next)
        composeTestRule.onNodeWithText(continueLabel).performClick()
    }
}

アプリの AndroidManifest.xml ファイルに ComponentActivity を追加する必要があるのでご注意ください。そのためには、次の依存関係をモジュールに追加します。

debugImplementation("androidx.compose.ui:ui-test-manifest:$compose_version")

カスタム セマンティクス プロパティ

カスタム セマンティクス プロパティを作成して、テストに情報を公開できます。作成するには、新しい SemanticsPropertyKey を定義し、SemanticsPropertyReceiver を使用して利用可能にします。

// Creates a semantics property of type Long.
val PickedDateKey = SemanticsPropertyKey<Long>("PickedDate")
var SemanticsPropertyReceiver.pickedDate by PickedDateKey

このプロパティを semantics 修飾子で使用します。

val datePickerValue by remember { mutableStateOf(0L) }
MyCustomDatePicker(
    modifier = Modifier.semantics { pickedDate = datePickerValue }
)

テストでは、SemanticsMatcher.expectValue を使用してプロパティの値をアサートします。

composeTestRule
    .onNode(SemanticsMatcher.expectValue(PickedDateKey, 1445378400)) // 2015-10-21
    .assertExists()

状態の復元を検証する

アクティビティまたはプロセスを再作成する場合、Compose 要素の状態が正しく復元されていることを検証します。StateRestorationTester クラスを使用すると、アクティビティの再作成に依存せずに、こうしたチェックを行えます。

このクラスを使用すると、コンポーザブルの再作成をシミュレートできます。特に、rememberSaveable の実装を検証するのに便利です。


class MyStateRestorationTests {

    @get:Rule
    val composeTestRule = createComposeRule()

    @Test
    fun onRecreation_stateIsRestored() {
        val restorationTester = StateRestorationTester(composeTestRule)

        restorationTester.setContent { MainScreen() }

        // TODO: Run actions that modify the state

        // Trigger a recreation
        restorationTester.emulateSavedInstanceStateRestore()

        // TODO: Verify that state has been correctly restored.
    }
}

さまざまなデバイス構成をテストする

Android アプリは、ウィンドウ サイズ、ロケール、フォントサイズ、ダークモードとライトモードなど、変化するさまざまな条件に適応する必要があります。これらの条件のほとんどは、ユーザーが制御するデバイスレベルの値から派生し、現在の Configuration インスタンスで公開されます。テストで異なる構成を直接テストするのは難しいです。これは、テストでデバイスレベルのプロパティを構成する必要があるためです。

DeviceConfigurationOverride はテスト専用の API で、テスト対象の @Composable コンテンツに対して、ローカライズされた方法でさまざまなデバイス構成をシミュレートできます。

DeviceConfigurationOverride のコンパニオン オブジェクトには、デバイスレベルの構成プロパティをオーバーライドする次の拡張機能があります。

特定のオーバーライドを適用するには、テスト対象のコンテンツを DeviceConfigurationOverride() トップレベル関数の呼び出しでラップし、適用するオーバーライドをパラメータとして渡します。

たとえば、次のコードは DeviceConfigurationOverride.ForcedSize() オーバーライドを適用して密度をローカルで変更し、テストを実行しているデバイスがそのウィンドウ サイズを直接サポートしていない場合でも、MyScreen コンポーザブルを強制的に大きな横向きのウィンドウでレンダリングします。

composeTestRule.setContent {
    DeviceConfigurationOverride(
        DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp))
    ) {
        MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
    }
}

複数のオーバーライドを同時に適用するには、DeviceConfigurationOverride.then() を使用します。

composeTestRule.setContent {
    DeviceConfigurationOverride(
        DeviceConfigurationOverride.FontScale(1.5f) then
            DeviceConfigurationOverride.FontWeightAdjustment(200)
    ) {
        Text(text = "text with increased scale and weight")
    }
}

参考情報

  • Android でアプリをテストする: Android テストのメイン ランディング ページでは、テストの基礎と手法について幅広く説明しています。
  • テストの基礎: Android アプリのテストに関する基本コンセプトを学びます。
  • ローカルテスト: 一部のテストは、自分のワークステーションでローカルに実行できます。
  • インストルメンテーション テスト: インストルメンテーション テストも実行することをおすすめします。つまり、デバイス上で直接実行されるテストです。
  • 継続的インテグレーション: 継続的インテグレーションを使用すると、テストをデプロイ パイプラインに統合できます。
  • さまざまな画面サイズをテストする: ユーザーが利用できるデバイスが多いため、さまざまな画面サイズでテストする必要があります。
  • Espresso: Espresso はビューベースの UI を対象としていますが、Compose テストの一部のアスペクトでも役立ちます。