E2Eテストの沼にハマったあなたへ。抜け出すためのtips集

# はじめに

テストが壊れやすい、遅い、不安定、失敗した原因を追うのがしんどい...
――E2Eテストに対してネガティブな印象を持っている人が多いのはそのせいかもしれません。
本来E2E（End-to-End）テストは、システム全体の動作を検証するための重要なテスト手法です。本記事では、E2Eテストの基本的な考え方と、E2Eテストの沼から抜け出すためのポイントを解説します。
テストレベルの比重はプロダクトによって異なりますが、**ここではテストピラミッドを理想として**執筆します。

# E2Eテストとは

E2Eテストとは、システムの入り口から出口までの一連の流れをテストし、エンドユーザーが期待する動作を保証するためのテストです。統合テストの一種で、エンドユーザーの操作を模倣し、フロントエンド、バックエンド、外部サービスとの連携を含めたテストを行います。


## 特徴

- **システム全体をテストする**
- **実際のユーザーの操作に近い**
- **テストの実行時間が長い**


## メリット

- **網羅性の高さ**  
  一回のテストシナリオで非常に多くのプロダクションコードを実行することができます。

- **リグレッションテストの一環として有効**  
  システムに新しい機能が追加されたり、変更が行われた際、E2Eテストを使用することで既存の機能に影響がないかを確認できます。特に、不具合を防ぐためのリグレッションテストとして非常に有効です。

- **複数のシステム間の連携を確認**  
  複数のシステムやサービス（例えば、フロントエンド、バックエンド、外部APIなど）が連携して動作するシナリオをテストできます。これにより、個別のシステムやコンポーネントが正しく統合・疎通されているかを確認することができ、統合時の不具合を早期に発見できます。

## デメリット

- **実行時間が長い**  
  他のテスト（単体テストや統合テスト）に比べて、E2Eテストは実行速度が遅くなりがちです。ブラウザの起動や実際のユーザー操作のシミュレーションが含まれているため、テスト時間が長くなることがあります。  
  フィードバックが遅いため、開発サイクルの低下につながる可能性があります。

- **不安定**  
  外部要因（ネットワークの遅延やサーバーの一時的な障害など）により、テスト結果が不安定になる場合があります。

- **メンテナンスコストが高い**  
  E2Eテストはシステム全体をテストするため、問題が発生した場合、その原因を特定するのが難しくなることがあります。UI、バックエンド、データベース、外部サービスなど、複数の要素が絡み合っているため、どこに問題があるのかを追跡するのに時間がかかります。その結果、開発者体験の低下につながります。

# E2Eテストを効果的に運用・沼から抜け出すためのtips

### **重要なシナリオに絞る**

すべての機能をE2Eテストで検証するのは実行速度・安定性・メンテナンスコストの観点から非効率です。  
単体テストで検証できないテストケースやユーザーが頻繁に利用する主要なフローにフォーカスしましょう。  
現場によっては、ビジネスの中核となる機能のみE2Eテストを書き、E2Eテストの数に制限を設けているところもあります。  
ドメインエキスパートやPdMなど含めてあらゆる視点から重要なシナリオを見つけましょう。

*tips: 最長のハッピーパスのみにする*  
主要なフローのうち、DBや外部サービスなど最も多くのシステムコンポーネントに依存するパスを選びます。これにより、システム全体が適切に連携していることを確認しつつ、シンプルで効率的に動作確認を行うことができます。

### **適切なタイミングで適切なテストを実行する**

一例ですが各テストファイルにタグをつけ、プルリクエストマージ時には必要最低限のクリティカルなテストのみを実行し、リリース前または夜間スケジューラーなどで全てのテストを実行するといった工夫ができます。  
適切なタイミングで適切なテストを実行することで、実行時間による開発者体験の低下を防ぐことができます。

### **安定性を高める**

シナリオはなるべく独立しているべきです。シナリオ間の依存関係はなるべく減らしましょう。  
テストデータはなるべくシナリオ間で干渉しないよう、データベースのクリーンアップや独立性を高めましょう。また、必要に応じてリトライ機能を入れると良いでしょう。  
各シナリオを並列実行できるかどうかの観点でシナリオを記述していくと、独立性は高まります。

*tips: シナリオごとにデータベースを作成する*  
テストランナーのインスタンスが一つの場合、同じインスタンス上でテスト開始時にシナリオごとランダムな名前のデータベースを作成します。テスト終了後にデータベースは削除します。

### **不安定なテストを許容しない**

不安定なテストはむしろ混乱を招いてしまう原因になります。  
可能であれば、定期的に不安定なテストを安定させるようにメンテナンスしましょう。  
対象のテストファイルがテストとしての信頼性を失っている場合、いっそのことテストファイルを削除したり、アーカイブするなどしてお掃除しましょう。

*tips: 不安定なテストファイルのアーカイブ*  
不安定なシナリオだとしても、先人が何らかの意図を持って作成したため必ず意味はあるはずです。  
ファイルごと削除するのに抵抗がある方は、アーカイブ用のフォルダを作成したり、アーカイブタグなどを付与することで定期実行から不安定なテストファイルを実行対象外にしましょう。

### **高速化を目指す**

E2Eテストは実行に時間がかかります。そのため、並列実行を検討し、並列実行が可能である前提でテストシナリオを記述しましょう。  
ただし、並列化を行うとテスト実行サーバーのメモリなどが逼迫されるので、並列数やテストランナーのリソースを適切にコントロールする必要があります。
また、ユニットテストや統合テストなどE2Eよりも高速なテストレベルに移行したり、不要なテストシナリオを削除したりしてE2E自体のボリュームを削減しましょう。

### **仕様書として活用する**

定期的に実行されているテストファイルは生きた仕様書です。  
E2Eテストはエンドユーザーの操作フローを模倣したテストになります。だからこそ、動く仕様書として活用もしやすいです。ユーザーストーリーで記述することもあります。

*tips: 非エンジニアでも読める形式で記述する*  
エンドユーザー視点だからこそ、ドメインエキスパートやPdMなど非エンジニアと一緒に作成することができます。  
振る舞い駆動（BDD）を意識し、自然言語に近い形式で記述するとエンジニア以外のメンバーにも仕様が伝わりやすいです。  
アジャイル開発やドメイン駆動設計を取り入れている現場であれば、ビジネスサイドとの距離が近いため、テスト仕様書にユビキタス言語を利用したり観点の洗い出しを一緒に行うことが容易です。  
また、最近では仕様書として意識されたテストファイルをAIに読み込ませ、仕様確認ChatBotの作成も可能になります。


## おまけ：E2Eテストはあるものの、誰も回していない時

既存でE2Eテストは存在するものの、メンテナンスされず失敗しやすくなり回帰テストなどで回っていない場合があります。  

まずは以下のステップを参考に、「回せるテストはCIで回す」ことを目指すと良いでしょう。


1. 一旦全てのE2Eテストを回し、成功しているものと失敗しているものを振り分ける
2. 成功したシナリオのうち、最もシンプルで安定しているテストシナリオを一つピックアップする
3. CI環境が整っていない場合は整える
4. スケジューラーで先ほどピックアップしたテストを実行できるようにする
5. スケジューラーでCIを回すことができたら、少しずつ回せるテストシナリオをCIに追加していく

プルリクエストのマージ前にE2Eテストも回せることが理想ですが、最初のステップとして夜間にスケジューラーでE2EをCIで回すところから始めてみてはいかがでしょうか。


## 既存のプロダクションコードに自動テストコードがない場合

この場合、まずはE2Eテストなどの上位レベルのテストを活用して、既存の動作を担保します。その後、リファクタリングを進めながら、ユニットテストやインテグレーションテストなど、E2Eテストよりも低レベルのテストを増やしていきます。このアプローチにより、テストの比重を段階的にテストピラミッドに沿った形へシフトさせることができます。

# おわりに

E2Eテストは、デグレの防止や品質を保証する上で欠かせないテスト手法ですが、適切な設計と運用をしないとコストが高くなりがちです。本記事で紹介したポイントを意識して、効率的なE2Eテストの運用を目指しましょう。
# 参考
- [単体テストの考え方/使い方 ](https://www.amazon.co.jp/%E5%8D%98%E4%BD%93%E3%83%86%E3%82%B9%E3%83%88%E3%81%AE%E8%80%83%E3%81%88%E6%96%B9-%E4%BD%BF%E3%81%84%E6%96%B9-Vladimir-Khorikov/dp/4839981728)
- [Googleのソフトウェアエンジニアリング ―持続可能なプログラミングを支える技術、文化、プロセス](https://www.amazon.co.jp/Google%E3%81%AE%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%83%AA%E3%83%B3%E3%82%B0-%E2%80%95%E6%8C%81%E7%B6%9A%E5%8F%AF%E8%83%BD%E3%81%AA%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%82%92%E6%94%AF%E3%81%88%E3%82%8B%E6%8A%80%E8%A1%93%E3%80%81%E6%96%87%E5%8C%96%E3%80%81%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9-%E7%AB%B9%E8%BE%BA-%E9%9D%96%E6%98%AD/dp/4873119650/ref=sr_1_1?crid=3BWH41X66VWYB&dib=eyJ2IjoiMSJ9.h8yTmlGJrO2mkSKQM2GJdilxy2MPRRtAsvU2qUoKxf7SlwYTMuD_3QXuxxuyq2DQ4c6yUwPdwJrDRLnI1Hh_8ISEde8sKVEowaaObPWom8i5_3J_tM9nQsxZguhLhB3ryxz7K7O5spzCuHUMPvxtCBB5jVSZstUJ-g3-uKNmrFLjVR6bycAC10nwG8rBRJplduXLUMNkYXdcE4ShPHQ7TQYFJ_ZqK1neXKWPapAWwVuPstRvfS4lIJDphQqlN5AY2LgadKumD4XV4zqGjLRaI-93Hrab_t8QRimj9fVPD2c.76lZE3PxhYYYgTJMLXT9MuGzoWAxc_JT-_FBZL39r9g&dib_tag=se&keywords=google%E3%81%AE%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%83%AA%E3%83%B3%E3%82%B0&qid=1743841312&sprefix=google%E3%81%AE%E3%81%9D%E3%81%B5%2Caps%2C165&sr=8-1)
- [システム運用アンチパターン ―エンジニアがDevOpsで解決する組織・自動化・コミュニケーション](https://www.amazon.co.jp/%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E9%81%8B%E7%94%A8%E3%82%A2%E3%83%B3%E3%83%81%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3-%E2%80%95%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%8CDevOps%E3%81%A7%E8%A7%A3%E6%B1%BA%E3%81%99%E3%82%8B%E7%B5%84%E7%B9%94%E3%83%BB%E8%87%AA%E5%8B%95%E5%8C%96%E3%83%BB%E3%82%B3%E3%83%9F%E3%83%A5%E3%83%8B%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3-Jeffery-D-Smith/dp/4873119847/ref=sr_1_1?crid=WAWU39ZDHWD1&dib=eyJ2IjoiMSJ9.ZNiuMYiq7ShEXCp2kRLXjFVO3Ds4HjEKiU2hxCuX9sxJq5RyRo4GJI8GyqyN0Bhi012emnvBZp85n7BuKDCbR6wwgVx2ockrRKZfvNBegGZy_8lvgf0tYQCCwuqL0WjpjD8WiVgIj8lHjt0ZaugYW6s1PcPKjym96FF_ZgENyfeRAF_C61rI_0VNVDw_NYvVry-g-aa0IC0IWSuIYeCQnTRFWbFZl9wR87qmMOBrH71LDYAywEjjexLXWe_Ap4WD3KN9u8eAQW_XjKq0P1ZwtrZHjyo0KfoZ3uVm6hvNhNA.2EUI-DfUWKb7pa1J4esfno5cP5dBFC7hLFMeodp1rwk&dib_tag=se&keywords=devops+%E6%9C%AC&qid=1743841354&sprefix=devops%2Caps%2C165&sr=8-1)
-