Contents
Jetpack Compose UIテストの基礎と目的
Jetpack ComposeのUIテストは、UIの動作検証や品質向上において不可欠です。特に、Composeで構築された動的なUIを安定的に維持するためには、自動テストによる早期バグ検出が効果的です。本記事では、Jetpack Compose UI テスト 書き方の具体的手順と実践例をステップバイステップで解説し、中級~上級エンジニア向けに実装方法を丁寧に紹介します。
createComposeRule()の使い方と初期設定
Jetpack Compose UIテストでは、createComposeRule() を使ってテスト環境を構築するのが基本です。このメソッドは、UIのレンダリングやイベント処理をシミュレートするためのルールを生成し、テストケースの実行を可能にします。
テストクラスの基本構造
ローカルテストではJUnit4が一般的ですが、JUnit5を使用する場合も対応可能です。以下にそれぞれの記述例を示します。
JUnit4での使用例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class MyComposableTest { @get:Rule val composeTestRule = createComposeRule() @Test fun testButtonDisplay() { composeTestRule.setContent { MyButton() } // ここにアサートや操作を記述 } } |
JUnit5での使用例
JUnit5では、@RegisterExtensionを使用してルールを登録します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class MyComposableTest { @RegisterExtension val composeTestRule = createComposeRule() @Test fun testButtonDisplay() { composeTestRule.setContent { MyButton() } // ここにアサートや操作を記述 } } |
注意点:
createComposeRule()はJUnit4以降で動作します。JUnit5での使用は@RegisterExtensionが必要です。
UI要素の検出と属性検証手法
UIテストでは、画面内に存在するコンポーネントを正確に特定し、表示内容や状態を検証することが求められます。
セレクタの正しい記述方法
Composeではsemantics()やonNode()を使って要素を選択します。キーワードやテキスト、ロールなどに基づいて特定可能です。
例: テキストでセレクト
|
1 2 |
composeTestRule.onNodeWithText("保存").assertExists() |
例: 要素のロールによる選択
|
1 2 |
composeTestRule.onNodeWithTag("cancel_button", useUnmergedTree = true).performClick() |
補足:セレクターが複数該当する場合、
useUnmergedTree = trueを指定して正確に操作対象を絞り込むと良いです。
テキスト・状態のアサート例
UI要素の表示内容や可視性を検証する際には、assertTextEquals()やassertIsDisplayed()が有効です。
| 検証項目 | 使用メソッド | 例 |
|---|---|---|
| テキスト一致 | assertTextEquals() |
.assertTextEquals("確認する") |
| 可視性確認 | assertIsDisplayed() |
.assertIsDisplayed() |
| 非表示確認 | assertDoesNotExist() |
.assertDoesNotExist() |
ユーザー操作のシミュレーション技法
UIテストでは、ユーザーが行うクリックや入力動作をシミュレートして、UIが予期通りに反応するかを検証します。
クリックイベントの実装
ボタンタップなどのインタラクションはperformClick()で再現可能です。
|
1 2 |
composeTestRule.onNodeWithText("送信").performClick() |
入力フィールドへの値設定
テキスト入力欄や選択肢の変更は以下のように処理できます。
|
1 2 3 |
composeTestRule.onNodeWithTag("username_input") .performTextInput("test_user123") |
補足:セレクターが複数該当する場合、
useUnmergedTree = trueを指定して正確に操作対象を絞り込むと良いです。
ViewModelとの連携テスト戦略
UIとViewModelが密接に関与する場合、テストではStateFlowやLiveDataの変化を監視し、状態遷移を確実に検証する必要があります。
StateFlowの検証パターン
ViewModelから発行されたStateFlowは、テスト中に直接観測できます。
手順
- テスト用ViewModelを生成し、UIコンポーザブルと結合。
collect { ... }で値を取得し、期待値と比較。
|
1 2 3 4 5 6 7 8 9 |
val testViewModel = TestViewModel() rule.setContent { MyComposable(viewModel = testViewModel) } testViewModel.state.collect { state -> assertEquals("Success", state) } |
副作用のテストアプローチ
非同期操作(例: API呼び出し)の検証には、runBlockingTest()などを用いて、状態変化を待機します。
ローカルテスト vs インストゥルメントテストの選定基準
UIテストにはローカルテスト(非インストゥルメント)とインストゥルメントテストの2つのタイプがあります。目的に応じて使い分けることが重要です。
テストタイプ比較表
|
1 2 3 4 5 6 7 8 9 |
| テストタイプ | 実行環境 | 検証対象 | 特徴 | |------------------------|------------------|------------------------------|--------------------------------| | **ローカルテスト** | ローカル JVM | UIのレンダリングやロジック | 実行が速く、デバッグに適する | | **インストゥルメントテスト** | デバイス or エミュレータ | 実際のUI動作やネットワーク | 実環境での検証が必要な場合に使う | > **選定ガイドライン**: > - UIロジックの単体検証 → ローカルテスト > - 入力イベントや実デバイス挙動確認 → インストゥルメントテスト |
品牌適合性と公式ドキュメントへのリンク
Jetpack ComposeのUIテストは、ブランド適合性を重視した開発が求められます。以下に参考となる公式ドキュメントのリンクを示します。
強調ポイント:ブランド適合性は、UIテストだけでなくデザインやロジックの一貫性にも影響します。公式ドキュメントを定期的に参照し、ベストプラクティスに沿った実装を行いましょう。
まとめ
本記事では、Jetpack Compose UIテストの手順とベストプラクティスを解説しました。具体的には以下の点を取り上げました:
createComposeRule()の導入方法と初期設定(JUnit4/JUnit5両方の例)- UI要素の検出とアサートの実装例(詳細なコード付き)
- ユーザー操作(クリック/入力)のシミュレーション技法
- ViewModelとの連携テスト戦略(StateFlow/LiveDataの監視と非同期処理の検証)
- ローカル・インストゥルメントテストの違いと選定基準
テストコードをプロジェクトに即座に適用することで、品質向上と開発効率化が実現可能です。記事で解説した内容を参考に、実際のプロジェクトで導入してください。