ほとんどのAPIバグは珍しいものではありません。それは、フィールドの欠落、誤ったステータスコード、負荷時のタイムアウト、あるいは誰も契約を確認しなかったために出荷されてしまった破壊的変更などです。アドホックなテストは、これらのいくつかを図らずも発見しますが、戦略は意図的にそれらを捕らえます。
APIテスト戦略とは、何を、どのレイヤーで、デリバリーサイクルのどの段階でテストするかについての計画です。どのチェックをコミットごとに実行するか、どれを毎晩実行するか、どれをリリース前に実行するかを決定します。最小限のメンテナンスで最大限のカバレッジを得るために、どこに労力を費やすべきかを教えてくれます。
APIテスト戦略が実際に意味するもの
戦略は、1つのアサーションを記述する前に、4つの質問に答えます。
何をテストしますか? ビジネス価値を持つエンドポイントとフローです。チェックアウトAPIは、ヘルスチェックエンドポイントよりも多くのカバレッジを必要とします。エンドポイントをヒットするのがどれだけ簡単かではなく、リスクとトラフィックでランク付けしてください。
どのレイヤーで? 一部のチェックは単一のリクエストに属します。他のチェックは、2つまたは3つのサービスが相互に通信する必要があります。すべてのチェックを最上位レイヤーに配置すると、スイートが遅くなり、脆くなります。
いつ実行しますか? 高速なチェックはプッシュごとに実行されます。低速なチェックはスケジュールまたはリリース前に実行されます。両方を混在させると、フィードバックが遅くなるか、カバレッジが薄くなるかのどちらかになります。
何がパスと見なされますか? 200レスポンスのみをチェックするテストでは、ほとんど何もわかりません。期待するステータスコード、スキーマ、フィールド値、レスポンス時間を定義してください。
これらの4つの質問にAPIについて答えられるようになったら、戦略ができたことになります。このガイドの残りの部分では、詳細を説明します。
戦略がアドホックテストに勝る理由
アドホックテストとは、リクエストを送信し、レスポンスを目で確認し、次に進むことを意味します。デモには機能しますが、実際のサービスでは3つの理由から破綻します。
繰り返しができません。次の担当者が手動チェックを再実行できないため、リグレッションが再発します。保存された自動化テストは、常に同じ方法で実行されます。
ハッピーパスに偏りがちです。手動でテストする場合、期待通りに機能する部分をテストします。不正なペイロード、期限切れのトークン、10,000件のリストなどを送信することはめったにありません。これらが本番環境で問題を引き起こすケースです。
スケーリングしません。40のエンドポイントと5つの環境を持つサービスの場合、リリースごとに200の手動チェックが必要になります。これを手動で行う人はいないため、APIが成長するにつれてカバレッジは縮小します。戦略は、再現可能なカバレッジのために1回のセットアップコストを交換します。テストは一度書けば、その後はあなたが関与しなくても変更ごとに実行されます。
APIテストピラミッド
テストピラミッドは、各レイヤーでどれくらいのテストを書くべきかを示す経験則です。下層が広く、上層が狭くなります。
/\
/ \ エンドツーエンド / ワークフローテスト (少数、低速、高価値)
/----\
/ \ 統合 / 契約テスト (いくつか、中速)
/--------\
/ \ 単体 / 単一リクエストテスト (多数、高速、安価)
/____________\
最下層: 単一リクエストチェック。 各テストは1つのエンドポイントにヒットし、レスポンスをアサートします。これらは高速で、記述コストが低く、デバッグが簡単です。失敗した場合、どのエンドポイントが壊れたか正確にわかります。ほとんどのテストはここに配置されます。
中間層: 統合と契約チェック。 これらは、サービスが交換するデータの形式について合意していること、および2つまたは3つのシステムに触れるリクエストが正しい結果を返すことを検証します。より多くの可動部品が関与するため遅くなりますが、単一リクエストテストでは見逃される障害を捉えます。
最上層: エンドツーエンドワークフロー。 これらは、複数のエンドポイントにわたる完全なユーザー体験を実行します。注文を作成し、支払いをし、ステータスを確認するなどです。これらは最も高い信頼性をもたらし、維持コストも最も高いため、数を少なくし、重要なパスに限定してください。
チームが犯す間違いは、ピラミッドを逆転させることです。つまり、遅いエンドツーエンドテストの山があり、高速なテストがほとんどない状態です。これは、長いフィードバックループと不安定な障害を引き起こします。下位レイヤーで同じバグを捉えられる場合はいつでも、カバレッジをピラミッドの下に押し下げてください。
テストの種類とそれぞれが適用される時期
完全な戦略では、異なる種類の障害を対象とした複数のテストタイプを使用します。それぞれのチェック内容と、いつそれを使用すべきかを以下に示します。
機能テスト
機能テストは、エンドポイントがその仕様に記載されているとおりに機能することを確認します。有効なリクエストを送信し、ステータスコード、レスポンススキーマ、フィールド値をアサートします。これはスイートの基盤であり、新しいエンドポイントで最初に自動化するものです。より詳細な手順については、「API機能テスト」を参照してください。
ユーザーエンドポイントの機能チェックは次のようになります。
GET /api/users/42 HTTP/1.1
Host: api.example.com
Authorization: Bearer <token>
アサーション:
- ステータスが
200である。 - ボディが
Userスキーマと一致する。 idが42である。emailが有効なメール文字列である。
統合テスト
統合テストは、エンドポイントが連携して機能すること、およびAPIがその依存関係(データベース、支払いプロバイダー、ダウンストリームサービス)と正しく通信することを確認します。機能テストはモックされた依存関係では合格するかもしれませんが、実際の依存関係が配線されると失敗する可能性があります。統合テストはそのギャップを埋めます。詳細な方法は「API統合テスト」で説明されています。
リグレッションテスト
リグレッションテストは、変更後も以前に機能していたものが壊れていないことを確認するために、既存のスイートを再実行します。ここで、保存された自動化スイートがその価値を発揮します。新しいリグレッションテストを記述するのではなく、すでに持っているテストをスケジュールに従って、そしてリリース前に実行します。これをどのように構築するかについては、「リグレッションテスト」を参照してください。
契約テスト
契約テストは、APIのプロバイダーとコンシューマーが正確なリクエストとレスポンスの形式について合意していることを検証します。これにより、破壊的変更(フィールド名の変更、型変更、削除されたエンドポイント)がコンシューマーに到達する前に検出されます。他のチームや顧客が依存するAPIを公開している場合、契約テストはオプションではありません。詳細は「API契約テスト」に記載されています。
負荷およびパフォーマンステスト
負荷テストは、同時トラフィック下でのAPIの動作を測定します。95パーセンタイルでの応答時間、エラー率、スループット、およびパフォーマンスが低下し始めるポイントなどです。ローンチ前、既知のトラフィックスパイク前、そして定期的に実行して、ゆっくりとしたパフォーマンスの低下を検出します。これは機能テストとは別の分野であり、異なるツールを使用します。オプションについては、「負荷テストツール」を参照してください。
セキュリティテスト
セキュリティテストは、APIが拒否すべきものを拒否することを確認します。トークンなしのリクエスト、間違ったスコープのリクエスト、インジェクション試行、他のユーザーのデータへのアクセスなどです。機密データに触れるすべてのエンドポイントには、少なくとも認証と認可のチェックが必要です。一連のテクニックの全容は「APIセキュリティテスト」に記載されています。
各タイプの実行時期に関する大まかなガイドを以下に示します。
| テストタイプ | 検出対象 | 実行時期 |
|---|---|---|
| 機能 | 誤ったステータス、スキーマ、または値 | コミットごと |
| 統合 | サービス間フローの破損 | コミットごとまたは毎晩 |
| リグレッション | 既存の動作の新たな破損 | コミットごとおよびリリース前 |
| 契約 | インターフェースに対する破壊的変更 | プロバイダーでのコミットごと |
| 負荷 | トラフィック下での遅延と障害 | ローンチ前および定期的 |
| セキュリティ | 認証、インジェクション、データ漏洩 | リリース前および定期的 |
ポジティブケース、ネガティブケース、およびエッジケース
各エンドポイントについて、3種類の入力についてカバーします。2番目と3番目をスキップすることは、実際のスイートで最も一般的なギャップです。
ポジティブケースでは、有効な入力を送信し、成功を期待します。適切に形成されたボディを持つユーザー作成リクエストは、201と新しいレコードを返します。これらはエンドポイントが機能することを確認します。
ネガティブケースでは、無効な入力を送信し、クリーンで正しい失敗を期待します。必須フィールドが欠落している場合、500ではなく400を返す必要があります。トークンなしのリクエストは401を返す必要があります。所有していないレコードのリクエストは403または404を返す必要があり、決してレコードを返してはいけません。ネガティブテストは、APIが最もよく情報漏洩したりクラッシュしたりするエラー処理を検証します。
エッジケースは、有効な入力の境界を押し広げます。空のリスト、許容される最大文字列長、ゼロ、負の数、Unicode名、夏時間境界のタイムスタンプなどです。これらは、オフバイワンやオーバーフローのバグを発見します。
確かなルールとして、ポジティブテストごとに少なくとも1つのネガティブテストを作成してください。注文作成エンドポイントにハッピーパスのテストが1つあり、負の数量や顧客IDの欠落に対するテストがない場合、カバレッジは見た目よりも薄いことになります。
POST /api/orders HTTP/1.1
Content-Type: application/json
{ "customerId": "c_123", "quantity": -5 }
期待される結果: ステータス400、ボディにquantityという明確な検証エラーが含まれていること。これが201または500を返した場合、ハッピーパスのテストでは決して見つけられなかったバグを発見したことになります。
テストデータと環境
テストは、実行対象のデータと環境と同じくらい信頼できます。
専用のテストデータを使用してください。 本番レコードに対してテストしたり、特定の行の存在に依存したりしないでください。テストが必要とするデータを生成するか、実行開始時に既知のフィクスチャをシードしてください。現実的で多様な入力には、データジェネレーターが時間を節約します。現実的なAPIテストデータの作成方法については、「テストデータジェネレーター」を参照してください。
テストを独立させてください。 各テストは独自のステートを設定し、終了後にクリーンアップする必要があります。前のテストが実行されていることに依存するテストは、ランダムな順序で失敗するテストです。あるリクエストから次のリクエストに値を渡す必要がある場合(ID、トークンなど)、共有グローバルステートを介するのではなく、シナリオ内で明示的に行ってください。
環境を分離してください。 ローカル開発、CI、ステージング、本番用に個別の環境を維持してください。ベースURL、トークン、その他の設定を環境ごとに保存し、1つの変数を交換するだけでどこでも同じテストが実行されるようにします。サンドボックスと完全なテスト環境の違いを理解することで、適切なターゲットを選択するのに役立ちます。詳細については、「サンドボックスとテスト環境」を参照してください。
変数でパラメータ化してください。 ホストやシークレットをテストにハードコーディングしないでください。環境変数を参照することで、ローカル、ステージング、CIに対して同じシナリオを編集なしで実行できます。
シフトレフト:より多くだけでなく、より早くテストする
シフトレフトとは、テストをデリバリーサイクルの早い段階、コードが書かれた瞬間に近づけることを意味します。バグが発見されるのが遅ければ遅いほど、修正にかかるコストは高くなります。設計時に発見されたスキーマの不一致は5分で修正できますが、本番環境で発見された同じ不一致はインシデントになります。
テストをシフトレフトするための3つの実用的な動き:
最初に契約を設計します。 エンドポイントを構築する前に、APIのリクエストおよびレスポンススキーマを定義します。これにより、その契約からテストとモックを生成し、実装が存在する前にインターフェースのテストを開始できます。
バックエンドが未完成のうちにモックに対してテストします。 フロントエンドと統合の作業は、実際のエンドポイントを待つ必要はありません。スキーマから構築されたモックサーバーを使用すると、コンシューマーは並行して自らの側をテストできます。
コミットごとに高速チェックを実行します。 数秒で実行される機能テストと契約テストは、夜間バッチではなく、開発者のインナーループに含めるべきです。開発者が赤いテストを早く見つけるほど、修正コストは安くなります。
これに関する詳細な事例は、「API開発におけるシフトレフトテスト」にあります。そのメリットはシンプルです。バグは早く見つけるほどコストがかかりません。
CIでのテストの自動化
戦略は、あなたが関与しなくても実行される場合にのみ意味があります。目標は、プッシュごとにスイートを実行し、失敗時にマージをブロックし、読み取り可能なレポートを生成するパイプラインです。
APIテストのための典型的なCIパイプラインには3つのステージがあります。
- プッシュごと: 高速な機能テストと契約テストを実行します。いずれかのアサートが失敗した場合はビルドを失敗させます。これがゲートとなります。
- 毎晩またはリリース前: 遅い統合およびエンドツーエンドスイート、さらに負荷およびセキュリティチェックを実行します。
- 常に: 機械可読なレポート(JUnit XMLが一般的な形式)を公開し、CIダッシュボードで合格数と失敗数を表示します。
プッシュごとにAPIテストスイートを実行する最小限のGitHub Actionsジョブは次のようになります。
name: api-tests
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Run API tests
run: npm test
正確なコマンドは使用するツールによって異なります。パターンはどこでも同じです。コードをチェックアウトし、ランタイムを設定し、スイートを実行し、ゼロ以外の終了コードでビルドを失敗させます。キャッシング、環境シークレット、レポートを含むCIの完全な処理については、「CI/CDでAPIテストを自動化する方法」を参照してください。
Apidogが戦略にどう適合するか
上記の戦略はツールに依存しません。設計、テスト、モック、ドキュメンテーション用の別々のツールから組み立てることができます。そのコストはドリフトです。仕様、テスト、モックが同期しなくなり、それらを調整するのに時間がかかります。
Apidogはそれを一箇所に統合します。API契約を設計し、それに対してテストシナリオを記述し、同じスキーマからモックを生成し、ドキュメントを公開します。これらすべてが単一の信頼できる情報源から行われます。テストとモックが同じ契約から生まれるため、契約テストとシフトレフトテストは追加の作業ではなく、デフォルトになります。
戦略のCI部分については、Apidog CLIが保存されたテストシナリオとスイートをヘッドレスで実行します。これはNodeパッケージであるため、Nodeを実行できるCIステップに組み込むことができます。
インストールします:
npm install -g apidog-cli
CIで保存されたシナリオまたはスイートを実行します。実行コマンドはフラグベースです。ターゲットID、環境ID、および必要なレポーターを渡します。
apidog run \
--access-token "$APIDOG_ACCESS_TOKEN" \
-t <scenarioOrSuiteId> \
-e <environmentId> \
-r cli,html,junit
--access-tokenは実行を認証します。トークンをCIシークレットとして保存し、環境変数として参照してください。-tは実行したいシナリオ、フォルダ、またはスイートのIDです。-eは環境IDで、この値を交換するだけでステージング環境やCI環境に対して同じスイートが実行されます。-rはcli、html、json、junitから1つまたは複数のレポーターを選択します。junit出力はCIダッシュボードにフィードされ、htmlは人間が読めるレポートを提供します。
データ駆動型実行の場合、CLIをデータファイルまたは保存されたテストデータIDに指定します。
apidog run \
--access-token "$APIDOG_ACCESS_TOKEN" \
-t <scenarioId> \
-e <environmentId> \
-d ./data/users.csv \
-r cli,junit
レポートをクラウドにプッシュするには--upload-reportを追加し、特定のブランチに対して実行するには--branchを追加します。CLIは保存されたApidogのシナリオとスイートを実行します。これは対話型のリクエスト送信ツールではなく、負荷ジェネレーターでもないため、ピラミッドのパフォーマンスレイヤーには専用の負荷ツールと組み合わせてください。
スターター戦略チェックリスト
ゼロから戦略を構築する場合は、このリストを順番に実行してください。
- [ ] エンドポイントをリスクとトラフィックでランク付けします。まず上位のエンドポイントからカバーします。
- [ ] ランク付けされた各エンドポイントについて、ポジティブな機能テストを作成します。
- [ ] エンドポイントごとに少なくとも1つのネガティブテスト(認証不足、無効な入力など)を追加します。
- [ ] リスト、数値、または自由テキストを受け入れるエンドポイントにエッジケーステストを追加します。
- [ ] 他のチームまたは顧客が利用するAPIには、契約テストを追加します。
- [ ] 2つ以上のサービスにまたがるフローには、統合テストを追加します。
- [ ] 環境変数とシークレットを含む、個別の環境を設定します。
- [ ] 生成されたまたはシードされたテストデータを使用し、テストを独立させます。
- [ ] CIでプッシュごとに高速テストを実行し、失敗した場合はビルドを失敗させます。
- [ ] 低速なスイート(統合、負荷、セキュリティ)を毎晩またはリリース前にスケジュールします。
- [ ] 合否カウントが可視化されるようにJUnitレポートを公開します。
- [ ] APIが変更されたときにスイートをレビューし、削除されたエンドポイントのテストを削除します。
初日からこれらすべてが必要なわけではありません。最もリスクの高いエンドポイントに対して機能テストとネガティブテストから始め、CIで実行できるようにし、そこからスイートを拡張していきます。
FAQ
APIテスト戦略とテスト計画の違いは何ですか?
戦略は、どのテストタイプをどのレイヤーでいつ実行するかといった高レベルのアプローチです。テスト計画は、特定のリリースや機能に対する具体的なドキュメントであり、正確なエンドポイント、ケース、データ、合格基準などを定めます。戦略は安定していますが、計画はリリースごとに変更されます。
ピラミッドの各レイヤーにはいくつのテストが必要ですか?
固定された比率はありませんが、数よりも形が重要です。ほとんどのテストは、最下層の高速な単一リクエストチェックであるべきで、中間層には統合テストと契約テストが少なく、最上層には少数のエンドツーエンドワークフローテストのみが配置されるべきです。遅い最上層のテストが高速な最下層のテストよりも多い場合は、バランスを再調整してください。
すでに機能テストがある場合でも契約テストは必要ですか?
はい、他のチームや顧客があなたのAPIを利用している場合は必要です。機能テストはエンドポイントが正しく動作することを確認しますが、契約テストはインターフェースがコンシューマーを壊すような形で変更されていないことを確認します。変更によってエンドポイント自体は機能し続けても、それを呼び出すすべての人を壊してしまうことがあります。
負荷テストはどのくらいの頻度で実行すべきですか?
ローンチ前や既知のトラフィックスパイク前に実行し、パフォーマンスの低下を検出するために定期的に(週ごとまたは月ごと)スケジュールして実行してください。負荷テストは時間がかかり、リソースを多く消費するため、すべてのコミットで実行すべきではありません。CIでは高速レイヤーを維持し、負荷テストは別に実行してください。
戦略全体をCIで自動化できますか?
再現可能な部分は可能です。機能テスト、統合テスト、契約テスト、リグレッションテストは、パイプラインでクリーンに実行され、マージのゲートとなります。負荷テストとセキュリティテストは、時間がかかったり専用のインフラストラクチャが必要だったりするため、独自のスケジュールで実行されることが多いです。Apidog CLIのようなヘッドレステストランナーは、プッシュごとに保存されたシナリオを実行することでCIの半分をカバーします。
