コントラクトテスト
サービス間の API 仕様 (契約) が守られていることを自動検証するテスト手法
コントラクトテストとは
コントラクトテスト (Contract Testing) は、API の提供者 (Provider) と利用者 (Consumer) の間の「契約」(リクエスト/レスポンスの形式、ステータスコード、フィールド名) が守られていることを自動検証するテスト手法である。マイクロサービス間の結合テストの代替として使われる。
なぜ必要か
マイクロサービスでは、サービス A がサービス B の API を呼び出す。サービス B がレスポンスのフィールド名を userName から name に変更すると、サービス A が壊れる。しかし、サービス B の単体テストは通る。サービス A の単体テストもモックを使っているため通る。この「サービス間の契約違反」は、結合テストかコントラクトテストでしか検出できない。
結合テストは全サービスを起動する必要があり、遅くて不安定だ。コントラクトテストは各サービスを単独でテストできるため、高速で安定している。
結合テストとの比較
結合テストとの主な違いを以下に比較する。
| 観点 | コントラクトテスト | 結合テスト (E2E) |
|---|---|---|
| 実行速度 | 速い (各サービス単独) | 遅い (全サービス起動) |
| 障害の特定 | 契約違反の箇所が明確 | どこが壊れたか特定しにくい |
| 環境 | ローカルで実行可能 | テスト環境が必要 |
| 安定性 | 高い (外部依存なし) | 低い (ネットワーク、データに依存) |
| カバー範囲 | API の形式のみ | ビジネスロジック全体 |
コントラクトテストは結合テストを完全に置き換えるものではない。API の形式 (スキーマ) の検証に特化しており、ビジネスロジックの正しさは検証しない。両方を組み合わせるのが理想だ。
Consumer-Driven Contract (CDC)
最も一般的なアプローチ。Consumer (利用者) が契約を定義し、Provider (提供者) がそれを満たすことを検証する。
1. Consumer 側: 契約を定義
// Pact を使った Consumer テスト
const provider = new PactV3({ consumer: 'OrderService', provider: 'UserService' });
provider.addInteraction({
states: [{ description: 'user 123 exists' }],
uponReceiving: 'a request for user 123',
withRequest: { method: 'GET', path: '/users/123' },
willRespondWith: {
status: 200,
body: { id: '123', name: string(), email: email() },
},
});
Consumer は「GET /users/123 を送ったら、id, name, email を含む 200 レスポンスが返ること」を契約として定義する。
2. Provider 側: 契約を検証
// Provider テスト: Consumer が定義した契約を満たすか検証
const verifier = new Verifier({
providerBaseUrl: 'http://localhost:3000',
pactUrls: ['./pacts/orderservice-userservice.json'],
});
await verifier.verifyProvider();
Provider のテストで、実際の API が契約を満たすレスポンスを返すことを検証する。
Provider-Driven Contract
Provider が OpenAPI 仕様書を公開し、Consumer がその仕様に準拠しているかを検証するアプローチ。API ファーストデザインとの相性が良い。
Lambda + API Gateway での活用
マイクロサービスを Lambda + API Gateway で構築している場合、各 Lambda 関数の入出力がコントラクトテストの対象になる。
- Consumer: フロントエンドや他の Lambda が期待するレスポンス形式を契約として定義
- Provider: Lambda 関数のテストで、契約を満たすレスポンスが返ることを検証
よくある失敗パターン
契約が古くなる
Consumer が契約を更新せず、Provider が新しいフィールドを追加しても検出されない。Pact Broker を使って契約を一元管理し、CI で自動検証する仕組みが必要だ。
過度に厳密な契約
レスポンスの全フィールドを厳密に検証すると、Provider の些細な変更 (新しいフィールドの追加) でテストが壊れる。Consumer が実際に使うフィールドだけを契約に含める。
実務での活用方法は関連書籍にも詳しい。
この記事は役に立ちましたか?
関連用語
API ファーストデザイン
実装の前に API の仕様を設計・合意し、フロントエンドとバックエンドの並行開発を可能にするアプローチ
マイクロサービス
1 つの大きなアプリケーションを複数の小さなサービスに分割し、それぞれが独立してデプロイ・スケール可能な状態で協調動作するアーキテクチャパターン
テスト駆動開発
テストを先に書き、テストが通るコードを実装し、リファクタリングするサイクルで開発を進める手法
テストピラミッド
単体テスト、結合テスト、E2E テストの比率を示すテスト戦略のモデル
スモークテスト
デプロイ後にシステムの基本機能が動作することを確認する簡易テスト
テスト戦略
プロジェクトのテスト種類、範囲、自動化レベルを体系的に定義する計画
関連する記事
テスト本ガイド - テスト設計を学べる技術書の選び方
テストの書き方からテスト戦略まで学べる技術書の選び方を紹介。テストピラミッド、TDD の正しい読み方、テストの ROI の考え方を解説します。
バグを生むのは知識不足ではなく想像力不足である
バグの多くは、コードを書いた時点で「こういうケースもありうる」と想像できなかったことが原因です。想像力を鍛える読書法と、エッジケースへの感度を高める方法を解説します。
「動くコード」と「良いコード」の間にある本
コードが動くようになった後、次に何を学べばよいのか。「動くコード」を「良いコード」に変えるために必要な知識と、それを効率的に学べる本の選び方を解説します。