テスト自動化実践ガイド
理想的な自動テスト
- 十分に高速
- 安定している(Flakyでない)
- 開発サイクルの中で実行できる
受け入れ条件
プロダクトバックログチケットなどを記載する際は、受け入れ条件(Acceptance Criteria)を決めることが大切
新機能開発であれば、どのような機能を作り、ユーザーがどのような操作をできるようになれば、その機能開発が「完了」とみなせるか具体的に定義する
自動テストをソースコードリポジトリに置く
E2Eテストはたいてい実際に利用するDatabaseへ接続することになる
そのAPIリポジトリが管理下における外部依存は直接利用すべきであり、逆に管理下にない依存はMock化すべきという意見もある
APIのソースコードを実際にローカルへデプロイし、Databaseは実際の環境のものを利用する
そしてローカルのエンドポイントを呼び出すことで自動テストを実行できるようにすれば、「理想的な自動テスト」の条件をより満たすことができそう
自動テストレベルで実際のDatabaseプロセスを使うのは大切かも
→Emulator, Dockerコンテナプロセス等を利用することも考えられるが、本番環境に近い環境である方がGapが少ないので、本番への流出を防げる可能性がより高まる
Point
- 単体テストとは実行コマンドや実行タイミングを変えてあげる
- 外部依存を利用する場合、1プロセスで完結する単体テストと比較するとFlakyになる
- ローカル環境で実行できることで開発サイクルに組み込んでもリズムが損なわれない
腐りやすいE2Eテスト
- アプリケーションの変更に対してテストが追従しない
- 頻繁なメンテナンスが必要になる
Google コードレビューガイドライン
以下は日本語版だが、英語版のものが正式なもの(最新が反映されている)
E2Eテストのリトライ
テストの不規則な失敗がネットワークの遅延等によりランダムに起きる場合、リトライは有効な選択肢となる。
例えば、「3回の試行で1回でも成功すればテスト成功と判断する」というようなルールを設定しておくなど。
複数プロセス(例えば、APIプロセスとDBプロセス)間で通信が発生するようなシナリオをE2Eテストで検証する場合は必ずネットワークを経由する必要があり、Flakyになる。
確率は低いかもしれないが、不安定に失敗する可能性があるため、リトライを設定することは有効な選択肢になる可能性が高い。
単体テスト/結合テスト/E2Eテスト
書籍では、バックエンドにおける各テストの違いについて、おおよそ以下のように記載しています。
(※注意書きとして、各テスト定義はチームごとの状況に応じて定義すべきともある)
単体テスト
- バリデーターなど個別ロジックのテスト
結合テスト
- APIエンドポイントなど、クライアント側から利用される単位でのテスト
E2Eテスト
- ビジネスプロセスベーステスト
- ユーザーストーリーベーステスト
- ユースケースベーステスト
書籍では、単体テスト/結合テスト/E2Eテストにおいて、最初はアイスクリームコーン型だったとしても、その検証観点を鑑みて1階層下へどんどん移譲していき、最終的にピラミッド型を目指すのが良いのではという記載が多々ある
つまり、上の階層ほど検証観点を絞るべきだし、重複観点は必ずしも上階層で検証する必要はないという見解
テスト用にAPIエンドポイントを露出する
- E2Eテストにおいて事前準備をUI自動操作で実施する場合、非常に時間がかかる
- 準備にAPIを利用すると高速化・安定化が期待できる
- 利用するAPIは、機能として提供されているものはもちろんOK
- テスト用に準備するのもOKだが、一般ユーザが利用できないよう認証をかけるべき
注意点として、UI操作で生成されるレコード と テスト用API呼出で生成されるレコード が異なると検証結果の信頼性が著しく低下するので、呼び出すクラスは統一するなどする
E2Eを実装するための著名なライブラリは?
E2Eテストには例えば以下のような機能があると便利である。
- 不安定な処理ではリトライを簡単に実装できる
- NWを介す処理は不安定になりやすいため
- 複数ケースを並列に実行できる
- 複数プロセスをNW経由で跨ぐテストは実行時間が長いため
- 開発環境/CI/ステージング環境 など各ステージごとに実行するテストを分けられる
- 開発サイクルにE2Eも組み込むと仮定するとサイクルが早い方が良く、時間かかるものは後回しにしたい
それらは自前で実装することも可能だが、ライブラリとして提供されていれば、より便利な気がしている。
既にあるはずなので探してみる
シンセティックモニタリング
NewRelic や Datadog などの監視ツールに含まれている機能。
紹介されたE2Eテストのようなコードを記述し、監視ツール上からそのコードを実行することで、テストが失敗したら知らせる機能。
E2Eテストは別リポジトリとして管理するのではなく、apiなどのリポジトリ上に別フォルダ(またはtestフォルダ配下、もしくはe2eディレクトリを切って*_test.go)とすることによって、
ライブラリアップデートなどの対応をe2e用リポジトリで行う必要も無いので陳腐化しないし、定期的に確実に最新ライブラリにされる。
利用するprotoバージョンやその他ライブラリバージョンも必ずProductionコードと同一であることが保証されるので、検証結果に信頼性も置ける。
コードカバレッジの種類
1. C0カバレッジ (Statement Coverage / ステートメントカバレッジ)
概要: 実行されたステートメント(命令)が全体の何パーセントかを測定します。
- 目的: コード内の各ステートメントが少なくとも一度実行されたかを確認する。
- 利点: 基本的なカバレッジ測定に適している。
- 欠点: 条件分岐のパスはカバーしない。
2. C1カバレッジ (Branch Coverage / 分岐カバレッジ)
概要: 条件分岐の各パス(true/false)が全体の何パーセントカバーされたかを測定します。
- 目的: すべての条件分岐が少なくとも一度実行されることを保証する。
- 利点: 分岐が漏れなくテストされる。
- 欠点: 複数条件の詳細な組み合わせまでは測定できない。
3. C2カバレッジ (Path Coverage / パスカバレッジ)
概要: 実行可能なすべてのコードパスを網羅することを測定します。
- 目的: 条件分岐の組み合わせも含めて、全ての実行パスがテストされることを確認する。
- 利点: 非常に詳細で厳密なテストが可能。
- 欠点: 実行パスが増えると計算的に非常に複雑になる。
4. 条件カバレッジ (Condition Coverage)
概要: 各条件式の結果(true/false)がカバーされたかを測定します。
- 目的: 各条件の個別評価を保証する。
-
例:
if (A && B)の場合、AとBそれぞれがtrue/falseになるケースを確認する。 - 利点: 各条件に対するフォーカスが可能。
- 欠点: 条件の組み合わせは測定されない。
5. MC/DCカバレッジ (Modified Condition/Decision Coverage)
概要: 各条件が他の条件に影響されず、単独で分岐結果に影響を与えるかどうかを測定します。
- 目的: すべての条件が分岐の結果に独立して寄与することを確認する。
- 利点: 航空・医療などの高信頼性が求められる分野で使われる。
- 欠点: 複雑なテストケースが必要。
6. ファンクションカバレッジ (Function Coverage)
概要: コード内の各関数が実行されたかを測定します。
- 目的: 全関数が最低一度は実行されることを保証する。
- 利点: 関数単位でのテスト進捗確認に便利。
- 欠点: 関数内の詳細なテスト状況は測定できない。
7. ラインカバレッジ (Line Coverage)
概要: ソースコードの各行がカバーされたかを測定します。
- C0に似ているが、コードの実行行数そのものを厳密に測定する点が異なる。
- 利点: 実行状況が視覚的にわかりやすい。
8. バグ発見のためのその他カバレッジ手法
データフローカバレッジ (Data Flow Coverage)
- 概要: 変数の定義と使用に注目して、値が適切に流れるか確認します。
- 例: 定義した変数が正しく使われ、消費されるかどうか。
トランザクションカバレッジ (Transaction Coverage)
- 概要: 一連の操作やプロセスの中で、全ての操作が正しく実行されたかを測定します。
Four Keys
- デプロイ頻度
- 正常なデプロイがどのくらいの頻度で行われているか
- リードタイム
- コードに変更を加えて(コミット)から本番環境に反映されるまでの所要時間
- 変更失敗率
- デプロイされた変更が何らかの問題を引き起こし、修正やRollbackが必要になった割合
- 平均復旧時間(Mean Time To Restore: MTTR)
- システムに障害が発生してから、それが解消されるまでの時間