このページでは、Android アプリをテストする際の基本方針について、中心となるおすすめの方法やメリットなどについて説明します。
テストのメリット
アプリの開発において、テストは非常に重要なプロセスです。アプリに対して一貫性のあるテストを実施することで、アプリの公開前に、その正確性、機能の動作、使いやすさを検証できます。
アプリを操作して手動でテストできます。さまざまなデバイスとエミュレータを使用し、システム言語を変更して、すべてのユーザーエラーを生成するか、すべてのユーザーフローを走査します。
ただし、手動テストはスケールが悪く、アプリの動作の回帰を見落とす可能性があります。自動テストでは、テストを実行するツールを使用します。このツールは高速で再現性が高く、通常は開発プロセスの早い段階でアプリに関する実用的なフィードバックを得ることができます。
Android のテストタイプ
モバイルアプリは複雑で、多くの環境で適切に動作する必要があります。そのため、さまざまな種類のテストがあります。
対象
たとえば、被写体に応じて、さまざまな種類のテストがあります。
- 機能テスト: アプリが想定どおりに動作するか?
- パフォーマンス テスト: 迅速かつ効率的に実施できるか?
- ユーザー補助機能のテスト: ユーザー補助サービスと連携して動作しますか?
- 互換性テスト: すべてのデバイスと API レベルで適切に動作しますか?
範囲
テストは、サイズや分離の程度によっても異なります。
- 単体テストまたは小規模テストでは、メソッドやクラスなど、アプリのごく一部のみを検証します。
- エンドツーエンド テストまたは大規模テストでは、画面全体やユーザーフローなど、アプリの広範な部分を同時に検証します。
- 中規模テストは、その中間に位置し、2 つ以上のユニット間の統合を確認します。
テストを分類する方法はたくさんあります。ただし、アプリ デベロッパーにとって最も重要な違いは、テストの実行場所です。
インストルメンテーション テストとローカルテスト
テストは Android デバイスまたは別のパソコンで実行できます。
- インストルメンテーション テストは、物理デバイスまたはエミュレートされた Android デバイスで実行されます。アプリは、コマンドを挿入して状態を読み取るテストアプリとともにビルドされ、インストールされます。計測テストは通常、アプリを起動して操作する UI テストです。
- ローカルテストは開発マシンまたはサーバーで実行されるため、ホストサイド テストとも呼ばれます。通常は小型で高速であり、テスト対象がアプリの他の部分から分離されます。
すべての単体テストがローカルであるとは限らず、すべてのエンドツーエンド テストがデバイスで実行されるとは限りません。次に例を示します。
- 大規模なローカルテスト: Robolectric など、ローカルで実行される Android シミュレータを使用できます。
- 小規模なインストルメンテーション テスト: SQLite データベースなどのフレームワーク機能でコードが適切に動作することを確認できます。このテストを複数のデバイスで実行して、複数バージョンの SQLite との統合を確認できます。
例
次のスニペットは、要素をクリックして別の要素が表示されることを確認するインストルメンテーション UI テストで UI を操作する方法を示しています。
Espresso
// When the Continue button is clicked
onView(withText("Continue"))
.perform(click())
// Then the Welcome screen is displayed
onView(withText("Welcome"))
.check(matches(isDisplayed()))
Compose UI
// When the Continue button is clicked
composeTestRule.onNodeWithText("Continue").performClick()
// Then the Welcome screen is displayed
composeTestRule.onNodeWithText("Welcome").assertIsDisplayed()
このスニペットは、ViewModel の単体テストの一部を示しています(ローカル、ホストサイドのテスト)。
// Given an instance of MyViewModel
val viewModel = MyViewModel(myFakeDataRepository)
// When data is loaded
viewModel.loadData()
// Then it should be exposing data
assertTrue(viewModel.data != null)
テスト可能なアーキテクチャ
テスト可能なアプリ アーキテクチャでは、コードが構造に従っているため、コードのさまざまな部分を個別に簡単にテストできます。テスト可能なアーキテクチャには、読みやすさ、保守性、スケーラビリティ、再利用性など、他の利点があります。
テスト不可のアーキテクチャは、次のことを生み出します。
- テストの規模が大きくなり、速度が低下し、不安定になります。単体テストできないクラスは、大規模な統合テストまたは UI テストでカバーする必要があります。
- さまざまなシナリオをテストする機会が少ない。テストが長くなると実行時間が長くなるため、アプリのすべての状態をテストすることは現実的ではありません。
アーキテクチャ ガイドラインの詳細については、アプリ アーキテクチャ ガイドをご覧ください。
分離のアプローチ
関数、クラス、またはモジュールの一部を残りの部分から抽出できると、テストは簡単で、より効率的になります。この方法は分離と呼ばれ、テスト可能なアーキテクチャにとって最も重要なコンセプトです。
一般的な分離手法には次のようなものがあります。
- アプリをプレゼンテーション、ドメイン、データなどのレイヤに分割します。アプリを機能ごとに 1 つずつモジュールに分割することもできます。
- アクティビティやフラグメントなど、依存関係が大きいエンティティにロジックを追加しないでください。これらのクラスをフレームワークへのエントリ ポイントとして使用し、UI とビジネス ロジックを、Compose、ViewModel、ドメインレイヤなど、他の場所に移動します。
- ビジネス ロジックを含むクラスでフレームワークの依存関係を直接使用しないでください。たとえば、ViewModel で Android コンテキストを使用しないでください。
- 依存関係を簡単に置換できるようにする。たとえば、具体的な実装ではなくインターフェースを使用します。DI フレームワークを使用していない場合でも、依存性注入を使用してください。
次のステップ
テストが必要な理由と、主な 2 種類のテストについて理解できたところで、テストする内容を確認するか、テスト戦略について学びましょう。
初めてのテストを作成し、実践で学びたい場合は、テストの Codelab をご覧ください。