このブログは「4 Essential Types of Automated API Testing」を翻訳・一部加筆したものです。
アプリケーション・プログラミング・インターフェース(API)、特に本記事で取り上げるWeb APIは、現代の多くのWebアプリケーションの基盤を成しています。これらのAPIには、ビジネス上の重要なロジックの多くが含まれており、たとえば複雑なアルゴリズム、データの取得や保存、他システムとの連携など、アプリケーションの頭脳とも言える役割を果たしています。
システムがより分散化されるにつれて、企業はWeb、モバイル、API間連携、さらにはModel Context Protocol(MCP)サーバーなど、複数のインターフェースを通じたシステム利用に対応する必要があります。そのため、安定して動作するAPIの確保は、ビジネス運用において極めて重要です。そしてこの安定性の実現において、テスト、特に自動化テストが大きな役割を果たします。
しかし、単にデータを送ってレスポンスを検証するだけがAPIテストではありません。それ以上に多様なアプローチを取ることが可能です。
本記事では、自動APIテストにおける4つの主要なテストタイプを紹介します:
機能テスト(Functional Testing)
契約テスト(Contract Testing)
統合テスト(Integration Testing)
モックテスト(Mock Testing)
それぞれのテストがどのような目的を持ち、どんなリスクを軽減し、どのように包括的なAPIテスト戦略に貢献するかを見ていきましょう。
機能テストは、自動APIテストの中でも最も一般的なアプローチです。APIが期待通りに動作しているかを検証することに焦点を当てており、通常はAPIにデータを送信し、そのレスポンスを検証する形で行われます。この検証では、ステータスコードをチェックすることが基本であり、可能であればレスポンスボディの内容も確認します。
ここで重要なのは、自分たちがテストしているシステムがどのような構成かを正しく理解することです。APIは一般的に、ユーザーインターフェース(UI)とデータベースの中間層と説明されることが多く、それは事実です。ただし筆者としては、「API本体」と「APIの内部実装(サービス層)」を区別して捉えたいと思います。
多くのAPIは、Express.jsのようにアプリケーションコードをラップするライブラリやフレームワークによって実装されており、ルーティング、認証、HTTPエンドポイントの作成といった役割を担っています。こうしたライブラリのマークアップや関数を適切に使うことでAPIが正しく機能しますが、ビジネスロジックやデータの保存処理はライブラリの責任範囲外であり、それらは内部のサービス層で行われます。
多くのユーザーインターフェースは、ユーザーからデータを受け取り、必要な構造に整えてAPIに送信します。機能テストではこのUI層をバイパスし、自分たちでデータを組み立ててAPIに送信します。これにより、テストの対象をよりリスクの本質(主にビジネスロジック)に近づけることができます。システムによっては、APIの呼び出しがデータベースや他のAPIといった下流の処理に繋がることもありますが、UI層はテスト対象から除外されています。
このように、機能テストではシステム全体のかなり大きな部分をテストしていることになります。通常「エンドツーエンドテスト」といえばUIテストを指しますが、筆者は機能APIテストも「APIにおけるエンドツーエンドテスト」だと考えています。なぜなら、UIは介さないものの、アプリケーションの多くの処理を通過しているからです。それでいて、UI層をスキップしている分、実行は高速であり、テスト対象も絞られているため効率的です。ただし、システムの主要部分は依然として起動が必要です。
ただし、これはあくまで一般的な話であって、システム構成によってはモック化されたデータベースやサービスを使ってAPIをテストすることも可能です。そのようなテストは、筆者としてはユニットテストと呼びます。この観点からも、「API層」と「サービス層」を分けて考えるという筆者の考え方が重要になります。サービス層はクラスやオブジェクトで構成されており、ユニットテストに最適です。一方でAPI層を含めると、スコープが広がり、HTTP経由でのやりとりが必要になります。
つまり、どのようなリスクを軽減したいのかを明確にし、アプリケーションのどの層をテスト対象とするかを意識的に選ぶことが重要です。理想的にはユニットテストをすべき状況であっても、開発環境や設計の都合でそれができない場合には、API層でのテストを行うことが妥当な判断となります。
機能APIテストでは、ブラウザやサーバーと同様にAPIへリクエストを送信する動作をツールでシミュレートする必要があります。
一般的に、以下の要素を自分で作成・設定する必要があります:
URL
リクエスト先のAPIを識別するためのもので、エンドポイントを指定します。
HTTPメソッド(Verb)
サーバーに対して何を行ってほしいかを示します。
例:GET(取得)、POST(作成)、PUT(更新)など。
ヘッダー(Headers)
リクエストの文脈や付加情報を指定します。アプリケーションごとに異なりますが、
一般的には以下のような情報が含まれます:
認証情報(Authentication)
レスポンス形式の指定(例:Accept: application/json)
トラッキングやログ用のメタデータなど
ボディ(Body)
APIエンドポイントに処理させたいデータの内容を指定します。
特にPOSTやPUTなどのメソッドでは、このボディが重要な役割を果たします。
チームがWeb APIを開発している場合、機能テストはできるだけ早い段階で作成すべきです。
これらのテストは、UIテストやユニットテストなど他の種類のテストと組み合わせて使うことが推奨されます。
特に以下のような状況で有効です:
システムのより下位層ではリスクを軽減できない場合
テスト容易性(テスタビリティ)の制約で下位層のテストが困難な場合
機能APIテストを行うことで、次のような情報が得られます:
APIが正しく動作している(=生きている)こと
ビジネスロジックが正しく実行されていること
ステータスコードやエラーメッセージが正しくマッピングされていること
契約テストは、APIテストの中でもあまり活用されていないアプローチのひとつです。
このテストは、APIの提供者(プロバイダー)と利用者(コンシューマー)の間にある「契約(=仕様)」が守られているかどうかに注目します。
プロバイダー:通常、自社システムのAPI
コンシューマー:他のAPI、外部サービス、あるいはフロントエンド(UI)など
2つのシステムがデータをやりとりする際、コードは特定の形式でデータが存在することを前提に書かれています。この前提こそが「契約」であり、契約テストではその取り決めが一方的に破られていないかを確認します。
特に契約テストは、マイクロサービスアーキテクチャのようにAPI同士が密接に連携している環境で非常に効果を発揮します。
プロパティの削除・追加・名前変更など、契約に対するデータ構造の変更
コンシューマー側に影響を与える破壊的変更
APIの動作に関する誤解や曖昧さの解消
契約テストには、Pactのような専用ツールを使う必要があります。
主に以下の2つのアプローチがあります:
コンシューマー側が契約内容を定義し、プロバイダーがそれを満たしているかを検証します。
契約には以下のような情報が含まれます:
テストの説明
対象状態(例:「Richard という名前のユーザーが存在する」)
リクエストの内容
期待されるレスポンスの構造
要するに、コンシューマーが自動テストの仕様を提示する形になります。
OpenAPIなどのAPI仕様書がある前提で、APIがそのスキーマ通りに動作しているかを検証します。
個々の値ではなく、データ型や構造が仕様に合っているか(例:idは数値、nameは文字列など)を確認します。
契約テストは以下のような状況で特に価値を発揮します:
APIを作るチームと使うチームが別である場合
外部の第三者(顧客やパートナー)がAPIを利用している場合
機能APIテストと比べて、作成・保守・実行が比較的簡単かつ高速であるため、初期段階から導入しやすいテスト手法です。
自動APIテストにおける統合テストは、機能テストと非常によく似ています。ただし、理想的な機能テストでは特定のエンドポイントにフォーカスし、下流の通信を極力制限しますが、統合テストではまさにその下流とのやり取りに伴うリスクを対象にします。
統合テストの目的は、APIが以下のような他システムとの連携において正しく動作しているかを確認することです:
他のAPI
データベース
メッセージキュー
外部サービスなど
統合テストを適切に実施するためには、システム構成や技術的な内部仕様への深い理解が欠かせません。これによって、リスクにフォーカスしたテスト設計が可能になります。
サービス間のデータフロー
他のサービスから正しいデータが渡され、正しく解釈・処理されているか。
構成ミス(設定の不備)
他のサービスとの統合に必要な設定(認証情報など)が正しく行われているか。
パフォーマンスの問題
実際の外部サービスとの連携によってタイムアウトや遅延などの問題が発生していないか。
統合テストでは、実際の第三者サービスを含めたシステム全体のデプロイが必要になります。ツール自体は、機能テストで使用するものと同じものを使うことができます。
ただし、以下の点において実施の複雑さが増します:
外部サービスのテスト/サンドボックス環境との接続設定
システム全体を通して整合性の取れたテストデータの管理
モックではなく実データのやり取りが発生するため、検証精度が高い反面、テストの信頼性を担保する必要がある
APIの統合テストは、リリースプロセスにおいて非常に重要です。
他のAPIテスト(機能テストや契約テストなど)がすでに実施済みの場合、統合テストはスモークテスト(動作確認)のような位置付けになります。
他のAPIテストを実施しない場合、統合テストが唯一の品質保証手段になります。
また、外部のサードパーティAPIに依存しているが契約テストが行われていない場合には、統合テストを通じてそれらのAPIの健全性を確認する手段となります。
モックテストは、厳密にはAPIそのものをテストする方法ではありませんが、あなたのAPIに依存する他のシステムのテストや、外部サービスとの統合テストを実施する際に、実際の接続なしで検証するための手段として非常に有用です。
モックAPIとは、任意のシナリオをシミュレーションできるAPIの仮想的なインスタンスです。たとえば、常に404ステータスコードを返すように設定したり(これはスタブと呼ばれることもあります)、毎回同じデータを返すように設定することもできます。
これにより、テストの制御性が高まり、再現性の高い(=決定論的な)テストが可能になります。
さらに、より高度なモックサーバーを用意すれば、実際のAPIのようにデータの保存や、受け取った内容に応じたレスポンスを返すようにもできます。
また、スパイ(Spy)と呼ばれるモックAPIでは、何回呼び出されたかを記録する機能もあります。
モックAPIには、本物のAPIと常に同期させておくことが難しいという課題があります。本物のAPIに変更が入った場合、それをモック側にも反映しなければならず、手間がかかりミスが発生しやすくなります。
テストの制御性が向上し、通常のテストでは難しいエラーコードのシミュレーションなどが容易になります。
下流のAPIの影響を排除し、自分のAPIコードだけに焦点を当ててテストできます。
ほとんどのAPIフレームワークには何らかのモック機能が用意されており、または専用のスタンドアロンツールを使うことも可能です。
基本的な手順は以下の通りです:
実際のAPIを呼び出して、レスポンス(ステータスコード、ヘッダー、ボディなど)をキャプチャする
そのレスポンスを基に、同じ結果を返すモックAPIを構築する
必要に応じて複数のパターン(成功/失敗など)を用意し、テスト内容に応じてモックを切り替える
モックサーバーは、以下のような状況で特に効果を発揮します:
利用している外部APIを自分でコントロールできない場合
→ そのAPIの戻り値を仮定して、自分のシステム側の動作をテスト可能
あなたのAPIに依存している別チームが存在するが、実装がまだ完了していない場合
→ モックを提供することで、実装を待たずに開発やテストを並行できます
APIに対する自動テストにはさまざまなアプローチがあり、軽減したいリスクに応じて適切なテスト手法を選ぶことが重要です。
多くのテスト手法を知っていればいるほど、それだけ柔軟にリスクに対応できる選択肢が増えます。
さらに、システムの構造やアーキテクチャに関する深い技術的理解があれば、どの手法を使うべきか、あるいは使わなくてよいかを的確に判断できるようになります。
私たちのシステムが複雑化・多層化・外部との連携強化が進む中で、小さく的を絞ったテストを設計・実装するには、あらゆるテスト手法を活用する必要があります。
そのようにすることで、APIの信頼性と機能性を確保しながら、素早く作成・実行・保守・デバッグ可能なテストスイートを構築できます。
私が最も効果的だと考えるのは、多層的なAPIテスト戦略です。
具体的には、以下のような手法を組み合わせて実施します:
機能テスト(Functional Testing)
契約テスト(Contract Testing)
ユニットテスト(Unit Testing)
統合テスト(Integration Testing)
さらに、モック(Mock)を提供することで、実際の環境では再現が難しいシナリオもテスト可能にします。
これは、自動テストに関するシリーズの第3回です。本シリーズは、Richard Bradshaw氏とのコラボレーションにより、テスト自動化のさまざまな側面とその実践方法を掘り下げていきます。