コントラクトテスト

サービス間の 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 が実際に使うフィールドだけを契約に含める。

実務での活用方法は関連書籍にも詳しい。

関連用語