エンドポイントに小さな変更を加えて出荷したとします。コードはコンパイルされ、新機能は動作し、デプロイも完了しました。2日後、名前を変更したフィールドが以前は文字列だったのに今はオブジェクトになっているため、モバイルクライアントがクラッシュし始めました。誰もそれを壊すつもりはありませんでした。変更は局所的に見えました。しかし、そうではありませんでした。
APIリグレッションテストは、そのような種類の障害が誰にも影響を与える前に捕捉するために存在します。今日のAPIの動作を記述するテストスイートを保存し、変更があるたびにそのスイートを再実行します。応答の形式、ステータスコード、またはキー値が記録した内容から逸脱した場合、スイートは失敗し、正確な場所を通知します。このガイドでは、何をベースラインにするか、再利用可能なスイートを構築する方法、そしてCIで自動的に実行する方法を説明し、名前変更がインシデントになることを防ぎます。
APIリグレッションテストとは何か(そしてなぜAPIは退行するのか)
リグレッションテストとは、変更後に既存の動作がまだ維持されていることを確認するために、既に合格したテストを再実行することです。APIの場合、「既存の動作」とは、コンシューマーが依存する契約、つまりルート、ステータスコード、応答スキーマ、主要フィールドの値です。
APIは、ごく普通の理由で退行します。誰かがJSONフィールドの名前を変更したり、リファクタリングによって200が204に変わったり、新しいバリデーションルールによって以前は受け入れられていた入力が拒否されたりすることがあります。ORMのアップグレードによって日付のフォーマットが静かに変更されたり、依存関係の更新によってクライアントがパースするエラーメッセージが変更されたりすることもあります。これらはいずれもコンパイルエラーとしては現れません。それらは統合の破損として現れ、しばしば呼び出し元の一部にのみ影響します。
ここで重要な区別は、APIリグレッションテストが一般的なソフトウェアリグレッションテストよりも範囲が狭いということです。アプリケーション全体のビジネスロジックを再検証するわけではありません。HTTPインターフェース、つまり他のシステムが結合する部分が変更されていないことを確認します。この重点化こそが、すべてのコミットで安価に実行できる理由です。
何をベースラインにするか
リグレッションスイートは、それがアサートする内容によってのみ価値があります。アサートが少なすぎると、実際の破損が見過ごされてしまいます。アサートが多すぎると、意図的な変更のたびにスイートが赤くなります。コンシューマーが実際に気づくであろうフィールドを対象としましょう。
これら4つのレイヤーをベースラインにしてください。
- ステータスコード。 各エンドポイントは、既知の入力に対して既知のステータスを返すべきです。
200が500になったり、201が200になったりすることは、たとえボディが問題なく見えてもリグレッションです。 - 応答スキーマ。 応答の構造と型。フィールド名、ネスト、そして値が文字列、数値、配列、またはオブジェクトであるかどうか。スキーマの変更は、最も一般的なサイレントブレイクです。
- 主要フィールド値。 すべての値ではなく、契約上の意味を持つ値、つまり存在しなければならない
id、既知のセット内に留まらなければならないstatusenum、数値でなければならないtotalなどです。 - 契約。 OpenAPI仕様とライブ応答との関係。仕様で
emailが必須とされているのにAPIがそれを返さなくなった場合、それは契約違反です。API契約テストを参照して、仕様を信頼できる唯一の情報源にする方法を確認してください。
有用なルール:クライアントが破損するであろうことについてアサートし、今日のペイロードにたまたま含まれていることについてはアサートしないでください。createdAtタイムスタンプはリクエストごとに変わるため、その値ではなく、その型とフォーマットを固定します。
以下は、単一のエンドポイントに対する最小限のアサーションセットで、応答に対して実行するプレーンなチェックとして記述されています。
GET /v1/users/42 -> 200
body.id is present, type number
body.email is present, type string, matches email format
body.status is one of ["active", "pending", "suspended"]
body.roles type array
response time < 800 ms
これら5行が契約を記述しています。変更後にこれらがいずれか失敗した場合、リグレッションが発生しています。応答ボディに対するチェックの記述に関する詳細については、APIアサーションを参照してください。
手動 vs 自動リグレッションテスト
手動でリグレッションテストを行うことも可能です。変更後、APIクライアントを開き、保存されたいくつかのリクエストを再生し、応答を目視で確認します。これは1つのエンドポイントでは機能しますが、10個になると破綻します。人間は退屈なケースをスキップし、退屈なケースにこそリグレッションが隠れています。また、スケジュールされたジョブが依存関係の更新をマージする午前2時には、手動チェックは実行できません。
自動リグレッションテストは、人間をループから排除します。スイートを一度記録すれば、マシンがすべてのプッシュ、すべてのプルリクエスト、すべてのデプロイでそれを再実行します。その価値は、単一の実行速度にあるのではありません。それは、誰かが労力をかける価値があると判断することなく、スイートが毎回実行されるという点にあります。
トレードオフは事前の作業です。スイートを構築し、最新の状態に保つ必要があります。そのメンテナンスコストは実際に発生しますが、5秒のテストで捕捉できたはずの1回の本番環境でのインシデントのコストよりも小さいです。
| 手動 | 自動 | |
|---|---|---|
| 変更ごとに実行されるか | いいえ、規律に依存します | はい、デフォルトで |
| カバレッジ | 少数のエンドポイント | スイート全体 |
| 実行あたりのコスト | 人間の時間で数分 | 計算時間で数秒 |
| 午前2時の破損を捕捉するか | いいえ | はい |
| 初期労力 | 低 | 中程度 |
おもちゃのプロジェクト以外では、自動化しましょう。
再利用可能なリグレッションスイートの構築
リグレッションスイートとは、アサーション付きの保存されたリクエストの集合体であり、それらをまとめて実行できるようにグループ化されています。目標は再利用です。一度構築すれば、永続的に実行し、エンドポイントを追加するたびに拡張します。
変更に耐えうるようにスイートを構築しましょう。
- 機能ではなくリソースでグループ化する。 すべての
/usersテストをまとめ、すべての/ordersテストをまとめます。ユーザーサービスに手を加える際に、どのグループを監視すべきか分かります。 - 動的なものには環境変数を使用する。 ベースURL、認証トークン、テナントIDは、各リクエストにハードコードするのではなく、環境に含めます。これにより、1つの設定を切り替えるだけで、同じスイートをローカル、ステージング、本番環境に対して実行できます。
- 相互に依存するリクエストを連結する。 現実的なフローでは、リソースを作成し、読み取り、更新し、削除します。作成応答から
idを抽出し、次のリクエストに渡します。これにより、単独では現れないがシーケンス全体で現れるリグレッションを捕捉します。これは、リグレッションテストがAPI統合テストと重なる部分です。 - データでエッジケースを駆動する。 ほぼ同じ10個のリクエストを作成する代わりに、1つのリクエストを作成し、それに有効な値、空の値、境界値、既知の不正な値などの入力テーブルを与えます。各行がテストケースになります。データ駆動型テストは、カバレッジを広げつつスイートを小さく保ちます。
単一のバリデーションテスト用データテーブルがCSVでどのように見えるかを示します。
email,expectedStatus
alice@example.com,201
bob@test.co,201
not-an-email,422
,422
a@b,422
1つのリクエスト、5つのケース、ステータスコードに関する5つのアサーションです。新しいエッジケースが見つかったら行を追加し、新しいコードを書くことなくスイートを成長させます。
スイートを高速に保ちましょう。20分かかるリグレッションスイートはスキップされてしまいます。遅いサードパーティの依存関係はモックし、ツールが許す場合は独立したリクエストを並行して実行し、完全なエンドツーエンドのフローは、より小さく、遅い夜間実行のために取っておきましょう。
CIで変更ごとにスイートを実行する
ノートパソコン上にあるリグレッションスイートは、ノートパソコンしか保護しません。重要なのは、リグレッションを引き起こしうる同じイベント、つまりプルリクエストやメインブランチへのマージ時に、継続的インテグレーションで実行することです。
パターンはCIシステム全体で同じです。ヘッドレスランナーをインストールし、保存されたスイートを指し、どのアサーションが失敗してもビルドを失敗させます。Apidogはまさにこのためのコマンドラインランナーを提供しています。Nodeでインストールしてください。
npm install -g apidog-cli
次に、保存されたテストシナリオまたはスイートをそのIDで、選択された環境に対して実行し、レポートを出力します。
apidog run \
--access-token "$APIDOG_ACCESS_TOKEN" \
-t 123456 \
-e 789012 \
-r cli,html,junit
それを分解してみましょう。
--access-tokenは実行を認証します。トークンはCIシークレットとして保存し、リポジトリには決して入れないでください。-tは実行するシナリオ、フォルダ、またはスイートのIDです。-eは環境IDであり、1つの値を変更することで、同じスイートをステージングまたは本番環境にターゲット設定できます。-rはレポート形式を列挙します。cliはコンソールに出力し、htmlは読みやすいレポートを生成し、junitはCIがテストごとの合否を表示するために解析できるXMLを出力します。
ランナーはヘッドレスです。Nodeを実行できるあらゆるCIステップに適合するため、同じコマンドがGitHub Actions、GitLab CI、Jenkins、CircleCIで機能します。以下は、すべてのプルリクエストでスイートを実行するGitHub Actionsジョブです。
name: API Regression
on: [pull_request]
jobs:
regression:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '22'
- run: npm install -g apidog-cli
- run: |
apidog run \
--access-token "$APIDOG_ACCESS_TOKEN" \
-t 123456 \
-e 789012 \
-r cli,junit
env:
APIDOG_ACCESS_TOKEN: ${{ secrets.APIDOG_ACCESS_TOKEN }}
シナリオが失敗すると、ランナーはゼロ以外のコードで終了し、ジョブは赤くなります。誰かが確認するまでプルリクエストはブロックされます。完全なコピー&ペースト可能なパイプラインとより多くのCIパターンについては、CI/CD用Apidog CLIおよびGitHub ActionsでのAPIテストを自動化する方法を参照してください。
スイートにデータファイルを供給する場合は、-dで渡します。
apidog run \
--access-token "$APIDOG_ACCESS_TOKEN" \
-t 123456 \
-e 789012 \
-d ./test-data/emails.csv \
-r cli,junit
独自のAPIバージョンを持つ機能ブランチをテストしますか?デフォルトの代わりにそのブランチの保存済みスイートに対して実行するには、--branchを追加します。クラウドで実行履歴を保持するには、単独の--upload-reportフラグを追加します。
スキーマと契約の差分比較
アサーションは動作上のリグレッションを捕捉します。スキーマの差分比較は、デプロイ前であっても、定義上のリグレッションを捕捉します。
その考え方:あなたのOpenAPI仕様は、リポジトリ内のバージョン管理されたファイルです。誰かがそれを編集すると、新しい仕様と以前の仕様を比較して変更を分類します。オプションフィールドの追加は安全です。フィールドの削除、名前の変更、型の厳格化、またはオプションフィールドを必須にすることは破壊的な変更です。差分ツールはプルリクエストで破壊的な変更を特定できるため、レビューアはYAMLの羅列ではなく、「これはuser.phoneを削除します」と確認できます。
それをライブAPIに対する契約テストと組み合わせます。各実行で、実際の応答を現在のスキーマと照合して検証します。仕様がAPIがもはや返さないフィールドを約束している場合、またはAPIが仕様で禁止されている型を返す場合、チェックは失敗します。これは二重の保護です。仕様の差分比較は契約への意図的な編集を捕捉し、契約テストはコードが契約から逸脱していることを捕捉します。
破壊的な変更は常にバグであるとは限りません。意図的にフィールドを削除することもあります。差分比較の目的は、その決定を明確にし、クライアントを驚かせるのではなく、バージョン管理と非推奨化のプロセスを通じてそれをルーティングすることです。意図的に行う変更の処理については、大規模なAPIのバージョン管理と非推奨化の方法を参照してください。
Apidogがリグレッションスイートを実行する方法
Apidogは、上記の要素を1か所にまとめ、スイートと仕様を隣り合わせに保ちます。
テストシナリオは視覚的に構築します。リクエストを連結し、ある応答から次の応答に値を抽出し、ステータス、スキーマ、フィールド値に関するアサーションを添付します。API設計とテストが同じプロジェクト内に存在するため、スキーマを二度記述することなく、エンドポイントの保存されたスキーマに対して応答を検証できます。設計が変更されると、テストがチェックするスキーマもそれに合わせて変更されます。
データ駆動型のケースでは、CSVまたはJSONデータセットをシナリオに添付すると、Apidogは行ごとに1つのケースを実行します。関連するシナリオをテストスイートに保存することで、1回の実行でリソース全体またはフロー全体をテストできます。
apidog-cliランナーは、上記のように保存されたスイートをヘッドレスでCIに取り込みます。保存されたシナリオとスイートを実行します。これはインタラクティブなリクエスト送信ツールでもロードジェネレーターでもありません。その役割はただ1つ、リグレッションスイートを再生し、合格した内容を報告することです。この狭い範囲こそが、Nodeを実行できる任意のCIステップに組み込める理由です。CLIとスクリプト化されたワークフローについては、Apidog CLI CI/CDパイプラインガイドを参照してください。
スターターワークフロー
以下は、テスト戦略を再構築することなく今週から採用できる手順です。
- 最も多く呼び出される5つのエンドポイントを選択する。 リグレッションのリスクはトラフィックが集中する場所に集中します。そこから始めましょう。
- 各エンドポイントに対してアサーション付きのリクエストを保存する。 ステータスコード、応答スキーマ、および2〜3個の主要フィールド。これがあなたのベースラインです。
- 1つの連結フローを追加する。 コアリソースに対して作成、読み取り、更新、削除を行います。これにより、単独テストでは見落とされるクロスリクエストのリグレッションを捕捉します。
- バリデーションが重いエンドポイントにデータテーブルを追加する。 いくつかの有効な入力、空の入力、不正な入力を含めます。
- プルリクエスト時にCIに組み込む。
apidog-cliをインストールし、-r junitでスイートを実行し、失敗時にはマージをブロックします。 - バグが見つかったらスイートを成長させる。 見過ごされた本番環境のリグレッションはすべて新しいテストケースとなります。スイートは自身の見落としから強くなります。
ステップ1から4は午後の作業です。ステップ5は単一のCIファイルです。その後、スイートは自動的に実行され、現実が新しい障害モードを教えたときにのみ拡張します。このフィードバックループが全体のポイントです。インシデントになり得た名前変更が、プルリクエスト上の赤いチェックになるのです。
よくある質問
APIリグレッションテストは一般的なリグレッションテストとどう違うのですか? 一般的なリグレッションテストは、UIやビジネスロジックを含むシステム全体のアプリケーション動作を再検証します。APIリグレッションテストは、これをHTTPインターフェース、つまりルート、ステータスコード、応答スキーマ、主要フィールド値に絞り込みます。この狭い焦点により、すべてのコミットで実行できるほど高速に保たれ、他のシステムが結合する対象を正確にターゲットとします。
リグレッションスイートはどのくらいの頻度で実行すべきですか? APIに影響を与える可能性のあるすべての変更時に実行すべきです。実際には、すべてのプルリクエストとメインブランチへのすべてのマージ時に、CIで自動的に実行されます。プルリクエストには高速なコアスイートを、夜間ビルドにはより大規模なエンドツーエンド実行を用意することで、コミットごとのチェックを迅速に保ちます。
脆いスイートを避けるために何についてアサートすべきですか? コンシューマーが破損するであろうことについてアサートすべきです。ステータスコード、応答構造と型、IDやステータス列挙型のような契約上の意味を持つフィールドの値を固定します。タイムスタンプのようにリクエストごとに変化する値については、そのリテラル値ではなく、型とフォーマットについてアサートします。変動性の高いデータについて過度にアサートすることは、誤った失敗の主な原因です。
コードを書かずにAPIリグレッションテストを実行できますか? はい、可能です。Apidogのようなツールを使用すると、シナリオとアサーションを視覚的に構築し、apidog-cliを使用してCIでヘッドレスで実行できます。インターフェースを通じてスイートを一度保存すれば、コマンドラインランナーがそれをパイプラインで再生するため、手書きのテストハーネスなしでテストが自動的に実行されます。
意図的な破壊的変更はどのように処理すればよいですか? 予期せぬテスト失敗として表面化させるのではなく、バージョン管理と非推奨化のプロセスを通じて処理してください。スキーマの差分比較を使用してレビューで変更をフラグ付けし、エンドポイントをバージョンアップするか、最初にフィールドをオプションとして追加し、変更が意図され消費者に伝達された後に、意図的にリグレッションベースラインを更新します。
