コントラクトテスト

英語名 Contract Testing
読み方 コントラクト テスティング
難易度
所要時間 導入に2〜5日
提唱者 Pact(2013年、RealEstate.com.au)
目次

ひとことで言うと
#

APIの呼び出し側(Consumer)と提供側(Provider)の間で**「このリクエストにはこのレスポンスを返す」という契約(Contract)を定義し、自動テストで検証**する手法。サービスを単独でテストでき、結合テストの遅さと不安定さを解消する。

押さえておきたい用語
#

押さえておきたい用語
Consumer(コンシューマー)
APIを呼び出す側のサービスのこと。「こういうリクエストを送り、こういうレスポンスが欲しい」という期待を契約として定義する主体。
Provider(プロバイダー)
APIを提供する側のサービスのこと。Consumerが定義した契約を満たしているかを検証される対象。
Pact Broker
契約ファイルを一元管理するサーバーのこと。Consumer・Provider双方のバージョン管理と互換性チェックを仲介し、can-i-deployコマンドでデプロイ可否を自動判定する。
CDC(Consumer-Driven Contract)
Consumerが主導して契約を定義するアプローチのこと。Providerは「誰が何を必要としているか」を正確に把握でき、不要な破壊的変更を防げる。

コントラクトテストの全体像
#

コントラクトテスト:Consumer駆動の契約検証フロー
Consumer(呼び出し側)期待するリクエスト・レスポンスをテストとして記述するMockサーバーで高速・安定テスト→ 契約ファイル(Pact)を生成Provider(提供側)契約ファイルのリクエストを実際のAPIに送信して検証契約違反 → テスト失敗→ デプロイをブロックPact Broker(契約の一元管理)契約ファイルのバージョン管理Consumer↔Provider の互換性マトリクスcan-i-deploy「このバージョンをデプロイして大丈夫か?」を自動判定
コントラクトテスト導入フロー
1
CDC理解
Consumer駆動の契約テストの概念を把握
2
Consumer側テスト
Mockサーバーでテスト実行し契約を生成
3
Provider側検証
実APIに契約を送信して互換性を検証
CI統合
can-i-deployでデプロイ可否を自動判定

こんな悩みに効く
#

  • バックエンドAPIの変更が、フロントエンドやモバイルアプリを壊してしまう
  • 結合テストが遅くて不安定で、CIパイプラインのボトルネックになっている
  • サービス間のAPI仕様がドキュメントと実装で乖離している

基本の使い方
#

ステップ1: Consumer駆動の契約を理解する

コントラクトテストの主流はConsumer-Driven Contract Testing(CDC)

  • **Consumer(呼び出し側)**が「こういうリクエストを送って、こういうレスポンスが欲しい」を定義する
  • **Provider(提供側)**がその契約を満たしているかを検証する
  • 契約はJSON形式のファイル(Pact file)としてバージョン管理される

ポイント: Consumerが主導するのがポイント。Providerは「誰が何を必要としているか」を正確に把握できる。

ステップ2: Consumer側のテストを書く

Consumer側でMockサーバーに対してテストを実行し、契約ファイルを生成する。

  • テストフレームワーク(Pact、Spring Cloud Contract等)を導入
  • 「このエンドポイントにGETを送ると、こういうJSONが返る」をテストとして記述
  • テスト実行時にMockサーバーが契約通りのレスポンスを返し、Consumer側のコードが正しく動くことを検証
  • テスト成功後、契約ファイルが自動生成される

ポイント: Consumer側のテストはProviderを起動せずに実行可能。高速で安定。

ステップ3: Provider側で契約を検証する

生成された契約ファイルを使って、Providerが契約を満たしているか検証する。

  • 契約ファイルをPact BrokerまたはCI経由でProvider側に共有
  • Providerの実際のAPIに対して、契約で定義されたリクエストを送信
  • レスポンスが契約通りか(ステータスコード、ボディの構造、型)を自動検証

ポイント: Provider側のテストで契約違反が検出されたら、デプロイをブロックする。

ステップ4: CIパイプラインに統合する

コントラクトテストをCI/CDに組み込み、自動化する。

  • Pact Brokerで契約ファイルを一元管理
  • Consumer変更時: 新しい契約がProviderに破壊的影響を与えないかチェック
  • Provider変更時: 既存の全契約を満たしているかチェック
  • can-i-deployコマンドで、デプロイの安全性を自動判定

ポイント: **Pact Brokerのcan-i-deploy**が「このバージョンをデプロイしても大丈夫か?」に自動で答えてくれる。

具体例
#

例1:注文サービスと在庫サービスの間で破壊的変更を防ぐ

状況: 注文サービス(Consumer)が在庫サービス(Provider)のAPIを呼び出している。在庫サービスがレスポンスのフィールド名を変更したことに気づかず、本番で注文処理がエラーになった。

Consumer側(注文サービス)のテスト: リクエスト GET /api/stock/SKU-001 に対して、レスポンスに sku(文字列型)、quantity(整数型)、available(真偽値型)が含まれることを定義。

Provider側(在庫サービス)の検証: 在庫サービスがquantitystock_countにリネームしようとした時点でテストが失敗しデプロイがブロックされた。

結果: API互換性の破壊がデプロイ前に100%検出されるようになった。結合テスト環境のメンテナンスコストが80%削減

例2:フロントエンド3チームがバックエンドAPIの契約を共有する

状況: Web・iOS・Androidの3チームが同一のバックエンドAPIを利用。バックエンドチームがレスポンスを変更するたびに、3チーム中少なくとも1つで障害が発生していた(月平均4件)。

導入: 3チームがそれぞれConsumerとして契約を定義。Pact Brokerで一元管理。バックエンドのCIに3つの契約検証を組み込み。

効果: バックエンドチームはPRの時点で「この変更がWebに影響するがiOSには影響しない」と即座に把握可能に。API変更起因の障害が月4件→0件に。バックエンドチームの「誰が何を使っているか分からない」問題が完全に解消。

例3:結合テスト環境の廃止でCI時間を70%短縮する

状況: マイクロサービス8つの結合テスト環境を維持。全サービスを起動してのE2Eテストに45分かかり、環境の不安定さでCIの成功率は65%。月のインフラコストは**$2,000**。

コントラクトテスト導入後:

  • 各サービスペア間にPactテストを導入(合計12件の契約)
  • 各サービスのCIで個別にコントラクトテストを実行(平均2分
  • E2Eテストは主要な3フローに絞り、週1回のみ実行

結果: CI時間が45分→13分70%短縮)。成功率が65%→96%。結合テスト環境のインフラコストを月$2,000→$300に削減。

やりがちな失敗パターン
#

  1. 契約を細かく定義しすぎる — レスポンスの全フィールドの値を固定で検証すると、Providerの些細な変更でテストが壊れる。型とフィールドの存在だけを検証し、値は柔軟に
  2. Pact Brokerを使わない — 契約ファイルをGitで直接共有すると、バージョン管理が煩雑になる。Pact Brokerで一元管理する
  3. 結合テストの完全な代替と考える — コントラクトテストは「インターフェースの互換性」を保証するが、エンドツーエンドの業務フローは検証しない。少数の結合テスト + 多数のコントラクトテストの組み合わせが最適
  4. Provider側の検証をCI必須にしない — Consumer側だけテストしてProvider側を任意にすると、契約違反が本番に到達する。Provider側のCIにも必ず組み込む

まとめ
#

コントラクトテストは、マイクロサービス間のAPI互換性を 「テストで保証する」 仕組み。Consumerが契約を定義し、Providerがそれを検証するCDCアプローチで、デプロイ前に破壊的変更を確実に検出できる。重い結合テストの代わりに軽量なコントラクトテストを活用し、安全かつ高速なデプロイサイクルを実現しよう。