テストする必要があるものと除外できるものを決定します。
前回の記事では、テストケースの基本と、テストケースに含める内容について説明しました。この記事では、技術的な観点からテストケースの作成について詳しく説明し、各テストに含める内容と避けるべき内容について説明します。基本的には、「何をテストすべきか」「何をテストすべきでないか」という古くからの質問に対する答えを学びます。
一般的なガイドラインとパターン
単体テスト、統合テスト、エンドツーエンド テストのいずれを実施する場合でも、特定のパターンとポイントが重要であることに注意してください。これらの原則は、どちらのタイプのテストにも適用できるため、最初に検討することをおすすめします。
シンプルにする
テストを作成する際は、シンプルさを保つことが最も重要です。脳の容量を考慮することが重要です。メインの本番環境コードは大量のスペースを占有するため、複雑さを追加する余地はほとんどありません。これはテストの場合に特に重要です。
ヘッドスペースが少ない場合は、テストに集中しやすくなります。そのため、テストではシンプルさを優先することが重要です。実際、Yoni Goldberg の JavaScript テストのベスト プラクティスでは、テストは複雑な数式ではなく、アシスタントのように感じられるようにすることが重要であると強調されています。つまり、テストの意図をひと目で理解できるようにする必要があります。
テストの複雑さにかかわらず、すべてのタイプのテストでシンプルさを追求する必要があります。実際、テストが複雑であればあるほど、テストを簡素化することが重要になります。これを実現する 1 つの方法は、フラットなテスト設計です。テストをできるだけシンプルに保ち、必要なものだけをテストします。つまり、各テストには 1 つのテストケースのみを含め、テストケースは特定の 1 つの機能のテストに焦点を当てる必要があります。
失敗したテストを読んだときに、何が問題だったのか簡単に特定できるという観点から考えてみましょう。そのため、テストをシンプルでわかりやすくすることが重要です。これにより、問題が発生したときに迅速に特定して修正できます。
価値のあるものをテストする
フラットなテスト設計は、集中力を高め、テストの有意性を高めることにも役立ちます。カバレッジのためだけにテストを作成するのではなく、常に目的を持ってテストを作成してください。
実装の詳細をテストしない
テストの一般的な問題の 1 つは、コンポーネントでのセレクタの使用やエンドツーエンド テストなど、実装の詳細をテストするようにテストが設計されていることです。実装の詳細とは、コードのユーザーが通常使用しない、表示しない、または知らないものを指します。これにより、テストで偽陰性と偽陽性の 2 つの大きな問題が発生する可能性があります。
偽陰性は、テスト対象のコードが正しいにもかかわらず、テストが失敗した場合に発生します。これは、アプリケーション コードのリファクタリングにより実装の詳細が変更された場合に発生する可能性があります。一方、偽陽性は、テスト対象のコードが正しくなくてもテストが合格した場合に発生します。
この問題の解決策の一つは、さまざまなタイプのユーザーを考慮することです。エンドユーザーとデベロッパーのアプローチは異なる場合があり、コードを操作する方法も異なる場合があります。テストを計画する際は、ユーザーが表示または操作する内容を考慮し、実装の詳細ではなく、それらの内容に依存するようにテストを作成することが重要です。
たとえば、変更されにくいセレクタ(CSS セレクタではなく data-attributes)を選択すると、テストの信頼性が高まります。詳細については、Kent C. の記事をご覧ください。また、このトピックに関する記事は近日公開予定です。
モック: 制御を失わない
モックは、単体テストで使用される広範なコンセプトであり、統合テストでも使用されることがあります。架空のデータやコンポーネントを作成して、アプリケーションを完全に制御する依存関係をシミュレートします。これにより、分離されたテストが可能になります。
テストでモックを使用すると、予測可能性、懸念事項の分離、パフォーマンスを向上させることができます。また、人間の関与が必要なテスト(パスポートの確認など)を行う必要がある場合は、モックを使用してテストを隠す必要があります。これらの理由から、モックは検討すべき有用なツールです。
ただし、モックは実際のユーザー エクスペリエンスではなく、モックであるため、テストの精度に影響する可能性があります。そのため、モックとスタブを使用する際は注意が必要です。
エンドツーエンド テストでモックを使用する必要があるか
一般的に、そうではありません。ただし、モックは場合によっては命を救うこともあります。完全に排除しないでください。
サードパーティの決済プロバイダ サービスを含む機能のテストを作成するシナリオを想像してみてください。提供されたサンドボックス環境を使用しているため、実際の取引は行われません。残念ながら、サンドボックスが誤動作しているため、テストが失敗しています。修正は、お支払いプロバイダが行う必要があります。プロバイダが問題を解決するまでお待ちいただくしかありません。
この場合、制御できないサービスへの依存を減らすことがより有益な場合があります。テストの信頼度が低下するため、統合テストやエンドツーエンド テストではモックを慎重に使用することをおすすめします。
テストの詳細: 推奨事項と禁止事項
では、テストにはどのような内容が含まれているのでしょうか。テストの種類に違いはありますか?主なテストタイプに応じた特定の側面について詳しく見てみましょう。
優れた単体テストに含まれるものは何ですか?
理想的で効果的な単体テストは、次の条件を満たす必要があります。
- 特定の側面に集中する。
- 独立して動作します。
- 小規模なシナリオを対象としています。
- わかりやすい名前を使用する。
- 該当する場合は、AAA パターンに沿って対応します。
- 包括的なテスト カバレッジを保証します。
推奨 ✅ | ❌ しないでください |
---|---|
テストはできる限り小さくします。テストケースごとに 1 つの項目をテストします。 | 大規模な単位のテストを作成する。 |
テストは常に分離し、ユニット外の必要なものをモック化します。 | 他のコンポーネントやサービスを含める。 |
テストを独立した状態に保つ。 | 以前のテストを利用するか、テストデータを共有する。 |
さまざまなシナリオとパスをカバーします。 | ハッピーパスまたはネガティブ テストに限定します。 |
テストのタイトルはわかりやすいものにして、テストの内容をすぐに把握できるようにします。 | 関数名のみでテストするため、結果として testBuildFoo() または testGetId() としか説明できません。 |
特にこの段階では、コードカバレッジを高めたり、テストケースの範囲を広げたりすることを目標にします。 | すべてのクラスからデータベース(I/O)レベルまでテストします。 |
優れた統合テストに含まれるものは何ですか?
理想的な統合テストは、単体テストと一部の条件を共有します。ただし、考慮すべき点がいくつかあります。優れた統合テストは、次の条件を満たす必要があります。
- コンポーネント間のインタラクションをシミュレートする。
- 実際のシナリオを網羅し、モックまたはスタブを使用します。
- パフォーマンスを考慮する。
推奨 ✅ | ❌ しないでください |
---|---|
統合ポイントをテストする: 各ユニットが統合されたときに、スムーズに連携することを確認します。 | 各ユニットを個別にテストします。これが単体テストの目的です。 |
実際のシナリオをテストする: 実際のデータから得られたテストデータを使用します。 | 繰り返し生成される自動テストデータや、実際のユースケースを反映していないその他のデータを使用する。 |
外部依存関係にモックとスタブを使用して、完全なテストを制御します。 | サードパーティ サービスへの依存関係を作成する(外部サービスへのネットワーク リクエストなど)。 |
各テストの前後にクリーンアップ ルーティンを使用します。 | テスト内でクリーンアップ手段を使用し忘れると、適切なテスト分離がないため、テストの失敗や誤検出につながる可能性があります。 |
優れたエンドツーエンド テストには何が含まれますか?
包括的なエンドツーエンド テストでは、次のことが必要になります。
- ユーザー操作を複製する。
- 重要なシナリオを網羅する。
- 複数のレイヤにまたがる。
- 非同期オペレーションを管理する。
- 結果を確認します。
- パフォーマンスを考慮する。
推奨 ✅ | ❌ しないでください |
---|---|
API ドリブンのショートカットを使用する。詳細 | beforeEach フックを含むすべてのステップで UI 操作を使用します。 |
各テストの前にクリーンアップ ルーティンを使用します。副作用のリスクが高いため、単体テストや統合テストよりもテストの分離にさらに注意してください。 | 各テストの後にクリーンアップを忘れる。残った状態、データ、副作用をクリーンアップしないと、後で実行される他のテストに影響します。 |
エンドツーエンド テストをシステム テストと見なします。つまり、アプリケーション スタック全体をテストする必要があります。 | 各ユニットを個別にテストします。これが単体テストの目的です。 |
テスト内でモックを使用する場合は、最小限に抑えるか、使用しない。外部依存関係をモック化するかどうかを慎重に検討してください。 | モックに大きく依存している。 |
パフォーマンスとワークロードを考慮します。たとえば、同じテストで大規模なシナリオを過度にテストしないでください。 | ショートカットを使用せずに、大規模なワークフローをカバーする。 |