自動テストは、さまざまな方法でアプリの品質向上に役立ちます。たとえば、検証の実行、回帰の検出、互換性の確認に役立ちます。優れたテスト戦略により、自動テストを活用して、重要なメリットであるデベロッパーの生産性に集中できます。
チームは、インフラストラクチャの強化と組み合わせた体系的なテスト アプローチを使用することで、生産性を高めることができます。そうすることで、コードの動作に関するフィードバックをタイムリーに得ることができます。優れたテスト戦略には次のような特長があります。
- 問題をできるだけ早く検出します。
 - 迅速に実行されます。
 - 修正が必要な場合に明確な指示を提供します。
 
このページでは、実装するテストの種類、テストの実行場所、テストの実行頻度を決定するうえで役立つ情報を提供します。
テスト ピラミッド
最新のアプリケーションでは、テストをサイズ別に分類できます。小規模なテストはコードの小さな部分のみに焦点を当てるため、高速で信頼性が高くなります。大規模なテストは範囲が広く、保守が困難な複雑な設定が必要になります。ただし、大規模なテストは忠実度が高く、一度に多くの問題を検出できます。
*忠実度とは、テスト ランタイム環境と本番環境の類似性を指します。
  ほとんどのアプリでは、多くの小規模なテストと、比較的少数の大規模なテストを行う必要があります。各カテゴリのテストの分布は、ピラミッドを形成する必要があります。多数の小規模テストがベースを形成し、少数の大規模テストが先端を形成します。
バグのコストを最小限に抑える
優れたテスト戦略は、バグを見つけるためのコストを最小限に抑えながら、デベロッパーの生産性を最大限に高めます。
非効率的な戦略の例を考えてみましょう。ここでは、テストの数はピラミッド型に整理されていません。大規模なエンドツーエンド テストが多すぎ、コンポーネント UI テストが少なすぎます。
  つまり、マージ前に実行されるテストが少なすぎます。バグがある場合、夜間または週次のエンドツーエンド テストが実行されるまで、テストでバグが検出されないことがあります。
バグの特定と修正にかかる費用への影響と、テストを小規模で頻繁に行うことが重要な理由について、以下で説明します。
- バグが単体テストで検出された場合、通常は数分で修正されるため、コストは低くなります。
 - エンドツーエンド テストでは、同じバグを見つけるのに数日かかることがあります。これにより、複数のチームメンバーが関与することになり、全体的な生産性が低下し、リリースが遅れる可能性があります。このバグのコストは高くなります。
 
ただし、非効率的なテスト戦略でも、戦略がないよりはましです。バグがリリースされると、修正がユーザーのデバイスに反映されるまでに数週間かかることがあるため、フィードバック ループが最も長く、コストも最も高くなります。
スケーラブルなテスト戦略
テスト ピラミッドは、従来、次の 3 つのカテゴリに分類されてきました。
- 単体テスト
 - 統合テスト
 - エンドツーエンド テスト。
 
ただし、これらのコンセプトには明確な定義がないため、チームはカテゴリを別の方法で定義する場合があります(5 つのレイヤを使用するなど)。
  - 単体テストはホストマシンで実行され、Android フレームワークに依存しない単一の機能単位のロジックを検証します。
- 例: 数学関数のオフバイワン エラーを検証する。
 
 - コンポーネント テストでは、システムの他のコンポーネントとは独立して、モジュールまたはコンポーネントの機能や外観を検証します。単体テストとは異なり、コンポーネント テストの対象範囲は、個々のメソッドやクラスよりも上位の抽象化まで広がります。
- 例: カスタムボタンの Screenshot test
 
 - 機能テストでは、2 つ以上の独立したコンポーネントまたはモジュールのインタラクションを検証します。機能テストはより大規模で複雑であり、通常は機能レベルで動作します。
- 例: 画面の状態管理を検証する UI 動作テスト
 
 - アプリケーション テストでは、デプロイ可能なバイナリの形式でアプリケーション全体の機能が検証されます。テスト対象システムとして、テストフックを含む開発ビルドなどのデバッグ可能なバイナリを使用する大規模な統合テストです。
- 例: 折りたたみ式デバイスの構成変更を検証する UI 動作テスト、ローカライズ テスト、ユーザー補助機能テスト
 
 - リリース候補テストでは、リリースビルドの機能が検証されます。アプリケーション テストと似ていますが、アプリケーション バイナリが圧縮および最適化されている点が異なります。これらは、アプリを一般ユーザー アカウントや一般公開バックエンドに公開することなく、できる限り本番環境に近い環境で実行される大規模なエンドツーエンドの統合テストです。
- 例: クリティカル ユーザー ジャーニー、パフォーマンス テスト
 
 
この分類では、忠実度、時間、範囲、分離レベルが考慮されます。複数のレイヤにわたってさまざまな種類のテストを実施できます。たとえば、アプリケーション テストレイヤには、動作、スクリーンショット、パフォーマンスのテストを含めることができます。
範囲  | 
    ネットワーク アクセス  | 
    実行  | 
    ビルドの種類  | 
    ライフサイクル  | 
  |
|---|---|---|---|---|---|
単位  | 
    依存関係が最小限の単一のメソッドまたはクラス。  | 
    ×  | 
    ローカル  | 
    デバッグ可能  | 
    マージ前  | 
  
コンポーネント  | 
    モジュール レベルまたはコンポーネント レベル 複数のクラスを同時に  | 
    ×  | 
    ローカル  | 
    デバッグ可能  | 
    マージ前  | 
  
機能  | 
    機能レベル 他のチームが所有するコンポーネントとの統合  | 
    モック  | 
    Local  | 
    デバッグ可能  | 
    マージ前  | 
  
応用  | 
    アプリレベル 他のチームが所有する機能やサービスとの統合  | 
    モック  | 
    エミュレータ  | 
    デバッグ可能  | 
    マージ前  | 
  
リリース候補版  | 
    アプリレベル 他のチームが所有する機能やサービスとの統合  | 
    本番環境サーバー  | 
    エミュレータ  | 
    縮小化されたリリースビルド  | 
    マージ後  | 
  
テストカテゴリを決定する
経験則として、チームに適切なレベルのフィードバックを提供できるピラミッドの最下層を検討する必要があります。
たとえば、ログインフローの UI というこの機能の実装をテストする方法を考えてみましょう。テストする必要がある内容に応じて、異なるカテゴリを選択します。
テスト対象  | 
    テスト対象の説明  | 
    テストカテゴリ  | 
    テストの種類の例  | 
  
|---|---|---|---|
フォーム バリデータ ロジック  | 
    正規表現に対してメールアドレスを検証し、パスワード フィールドが入力されたことを確認するクラス。依存関係はありません。  | 
    単体テスト  | 
    |
ログイン フォームの UI の動作  | 
    フォームが検証された場合にのみ有効になるボタンを含むフォーム  | 
    コンポーネント テスト  | 
    Robolectric で実行される UI 動作テスト  | 
  
ログイン フォームの UI の外観  | 
    UX 仕様に準拠したフォーム  | 
    コンポーネント テスト  | 
    |
認証マネージャーとの統合  | 
    認証マネージャーに認証情報を送信し、さまざまなエラーを含む可能性のあるレスポンスを受信する UI。  | 
    機能テスト  | 
    |
ログイン ダイアログ  | 
    ログイン ボタンが押されたときにログイン フォームが表示される画面。  | 
    アプリケーション テスト  | 
    Robolectric で実行される UI 動作テスト  | 
  
クリティカル ユーザー ジャーニー: ログイン  | 
    ステージング サーバーに対するテスト アカウントを使用した完全なログインフロー  | 
    リリース候補版  | 
    デバイスで実行されるエンドツーエンドの Compose UI 動作テスト  | 
  
あるものがどのカテゴリに属するかは、主観的な判断になる場合もあります。インフラストラクチャの費用、脆弱性、テスト時間の長さなど、テストの優先度を上げたり下げたりする理由が他にもあります。
テスト カテゴリはテストの種類を規定するものではなく、すべてのカテゴリでテストする必要がある機能があるわけではありません。
手動テストもテスト戦略の一部にすることができます。通常、リリース候補のテストは QA チームが行いますが、他の段階にも関与する場合があります。たとえば、スクリプトを使用せずに機能のバグを探索的にテストします。
テスト インフラストラクチャ
テスト戦略は、デベロッパーがテストを継続的に実行し、すべてのテストが合格することを保証するルールを適用するのに役立つインフラストラクチャとツールによってサポートされる必要があります。
テストをスコープ別に分類して、どのテストをいつ、どこで実行するかを定義できます。たとえば、5 レイヤモデルに従う場合:
カテゴリ  | 
    環境(場所)  | 
    トリガー(タイミング)  | 
  
|---|---|---|
単位  | 
    [ローカル][4]  | 
    すべての commit  | 
  
コンポーネント  | 
    ローカル  | 
    すべての commit  | 
  
機能  | 
    ローカルとエミュレータ  | 
    マージ前(変更をマージまたは送信する前)  | 
  
応用  | 
    ローカル、エミュレータ、スマートフォン 1 台、折りたたみ式デバイス 1 台  | 
    マージ後、変更をマージまたは送信した後  | 
  
リリース候補版  | 
    8 種類のスマートフォン、1 種類の折りたたみ式デバイス、1 種類のタブレット  | 
    リリース前  | 
  
- 単体テストとコンポーネント テストは、新しいコミットごとに継続的インテグレーション システムで実行されますが、影響を受けるモジュールに対してのみ実行されます。
 - 変更を統合または送信する前に、すべての単体テスト、コンポーネント テスト、機能テストが実行されます。
 - アプリケーション テストはマージ後に実行されます。
 - リリース候補版のテストは、スマートフォン、折りたたみ式デバイス、タブレットで毎晩実行されます。
 - リリース前に、リリース候補のテストが多数のデバイスで実行されます。
 
テストの数が生産性に影響する場合は、これらのルールが変更されることがあります。たとえば、テストをナイトリー ケイデンスに移行すると、CI ビルドとテストの時間を短縮できますが、フィードバック ループが長くなる可能性もあります。