AIサービスを開発ていると、こんな悩みをお持ちではないでしょうか。

これらの問題を解決するのがContract Testing(コントラクトテスト)です。

私は以前、新機能を追加したつもりが既存ユーザーに影響を与えてしまい、夜中に緊急対応を繰り返した経験があります。そんな自分を救ってくれたのが、このContract Testingです。

Contract Testingとは?身近な例で理解する

Contract Testingを一言でいえば「約束事のテスト」です。

あなたの町にImagineSpotという無人配送サービスがあるとします。あなたが「新鮮な野菜セット希望」とメッセージを送ると、ImagineSpotが「了解、3日後に届けます」と返信する約束でした。

ある日ImagineSpotが「3日後」を「5日後に変更」しても、あなたに通知しなければどうでしょう?あなたのアプリが「3日」と信じて開発したコードは、当然ながら動かなくなります。

Contract Testingとは、この約束事を明文化して、自动的にチェックする仕組みです。提供者(AI API)と使用者(あなたのアプリ)が「この形だよ」という合意を形成し、常に一致していることを確認します。

なぜAIサービスにContract Testingが必要か

HolySheep AIのようなマルチモデルプラットフォームでは、複数のモデル(GPT-4.1、Claude Sonnet 4.5、Gemini 2.5 Flash、DeepSeek V3.2など)を切り替えて使うことができます。しかし、各モデルはレスポンスの形式やフィールド名が微妙に異なります。

例えば、あるモデルが返すJSONに「usage」の代わりに「token_usage」というフィールドを返してきたら、あなたのコードはエラーを起こします。

Contract Testingを導入することで、

そんなメリットが得られます。

実践:HolySheep AIでContract Testingを動かしてみよう

準備するもの

ステップ1:プロジェクトを作成しよう

まず、作業を 위한フォルダを作成し、移动します。

mkdir ai-contract-test
cd ai-contract-test
npm init -y

💡 ヒント: ターミナルに「npm init -y」と入力すると、「package.json」というファイルが自動生成されます。スクリーンショットイメージ:コマンド入力後のコンソール表示

ステップ2:必要なパッケージをインストール

npm install @pact-foundation/pact axios dotenv

Pactは最もPopularなContract Testingフレームワークです。Provider(API提供者)とConsumer(API使用者)の間の「契約」を管理してくれます。

ステップ3:環境変数を設定

プロジェクトのルートフォルダに「.env」ファイルを作成し、以下の内容を記述します。

HOLYSHEEP_API_KEY=YOUR_HOLYSHEEP_API_KEY
HOLYSHEEP_BASE_URL=https://api.holysheep.ai/v1

💡 ヒント: .envファイルの編集は、メモ帳やVS Codeで開いて行えます。YOUR_HOLYSHEEP_API_KEYの部分は、HolySheep AIダッシュボードで確認した実際のAPIキーに置き換えてください。スクリーンショットイメージ:.envファイルの編集画面

ステップ4:Contract Testのコードを書く

「consumer.spec.js」というファイルを作成し、以下のコードを貼り付けます。

const { Pact } = require('@pact-foundation/pact');
const path = require('path');
const axios = require('axios');

// PactのMockサーバーを設定
const provider = new Pact({
  consumer: 'MyAIApp',
  provider: 'HolySheepAI',
  port: 1234,
  dir: path.resolve(__dirname, 'pacts'),
  logLevel: 'info'
});

describe('HolySheep AI Contract Test', () => {
  // テスト前にMockサーバーを起動
  before(() => provider.setup());

  // テスト後にMockサーバーを停止
  after(() => provider.finalize());

  afterEach(() => provider.removeInteractions());

  describe('Chat Completions API', () => {
    it('正常系: 有効なリクエストでレスポンスが返る', async () => {
      // 【Consumer側の期待値(Contract)】
      await provider.addInteraction({
        states: [{ description: 'server is healthy' }],
        uponReceiving: 'a request for chat completions',
        withRequest: {
          method: 'POST',
          path: '/chat/completions',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer matching(.+)'
          },
          body: {
            model: 'gpt-4o',
            messages: [
              { role: 'user', content: 'Hello' }
            ],
            max_tokens: 100
          }
        },
        willRespondWith: {
          status: 200,
          headers: {
            'Content-Type': 'application/json; charset=utf-8'
          },
          body: {
            id: 'chatcmpl-xxx',
            object: 'chat.completion',
            created: 1234567890,
            model: 'gpt-4o',
            choices: [
              {
                index: 0,
                message: {
                  role: 'assistant',
                  content: 'Hello! How can I help you today?'
                },
                finish_reason: 'stop'
              }
            ],
            usage: {
              prompt_tokens: 'matching(\\d+)',
              completion_tokens: 'matching(\\d+)',
              total_tokens: 'matching(\\d+)'
            }
          }
        }
      });

      // 実際のAPIコール(Mockサーバー経由)
      const response = await axios.post(
        'http://localhost:1234/v1/chat/completions',
        {
          model: 'gpt-4o',
          messages: [{ role: 'user', content: 'Hello' }],
          max_tokens: 100
        },
        {
          headers: {
            'Authorization': Bearer ${process.env.HOLYSHEEP_API_KEY},
            'Content-Type': 'application/json'
          }
        }
      );

      expect(response.status).to.equal(200);
      expect(response.data).to.have.property('choices');
      expect(response.data.choices[0]).to.have.property('message');
      expect(response.data.choices[0].message).to.have.property('content');
    });

    it('異常系: 無効なAPIキーでエラーが返る', async () => {
      await provider.addInteraction({
        uponReceiving: 'a request with invalid API key',
        withRequest: {
          method: 'POST',
          path: '/chat/completions',
          body: {
            model: 'gpt-4o',
            messages: [{ role: 'user', content: 'Test' }]
          }
        },
        willRespondWith: {
          status: 401,
          body: {
            error: {
              message: 'Incorrect API key provided',
              type: 'invalid_request_error',
              code: 401
            }
          }
        }
      });

      const response = await axios.post(
        'http://localhost:1234/v1/chat/completions',
        {
          model: 'gpt-4o',
          messages: [{ role: 'user', content: 'Test' }]
        },
        {
          headers: {
            'Authorization': 'Bearer invalid_key',
            'Content-Type': 'application/json'
          }
        }
      ).catch(err => err.response);

      expect(response.status).to.equal(401);
      expect(response.data.error).to.have.property('message');
    });
  });
});

💡 ヒント: このコードでは「usage」の値が数値であることを正規表現で検証しています。モデルは変わっても、この検証が通ればレスポンスの整合性は保証されます。スクリーンショットイメージ:テストコードの全体像

ステップ5:テストを実行

npx mocha consumer.spec.js --timeout 30000

テストに成功すると、以下のような出力が表示されます。


  HolySheep AI Contract Test
    Chat Completions API
      ✓ 正常系: 有効なリクエストでレスポンスが返る
      ✓ 異常系: 無効なAPIキーでエラーが返る

  2 passing (1.5s)

💡 ヒント: 绿色のチェックマーク(✓)が表示されれば成功です。赤い場合は、Contractと実際のレスポンスにズレがあります。スクリーンショットイメージ:テスト成功時のコンソール表示

Contractファイルを共有してチームで活用

テストが成功すると、「pacts」フォルダにJSONファイルが自動生成されます。このファイルをチームメンバーやCI/CDパイプラインと共有することで、全員が同じ「約束事」を基に開発を進められます。

{
  "consumer": { "name": "MyAIApp" },
  "provider": { "name": "HolySheepAI" },
  "interactions": [
    {
      "description": "a request for chat completions",
      "request": {
        "method": "POST",
        "path": "/v1/chat/completions"
      },
      "response": {
        "status": 200,
        "body": {
          "choices": [{ "message": { "content": "string" } }]
        }
      }
    }
  ]
}

HolySheep AIで始めるメリット

HolySheep AIでContract Testingを始めるべきか迷っている方に、私が魅力を感じているポイントをお伝えします。

特に私は何度もAPIを呼び出すテスト環境でも、コストを気にせず экспериメントできたことに感謝しています。

よくあるエラーと対処法

エラー1:401 Unauthorized - APIキーが認識されない

Error: Request failed with status code 401
Response: { "error": { "message": "You didn't provide an API key..." } }

原因:環境変数が正しく読み込まれていない、またはAPIキーが空です。

# 解決方法:.envファイルの内容を確認
cat .env

それでも駄目な場合、直接エクスポート

export HOLYSHEEP_API_KEY="your_actual_api_key_here" export HOLYSHEEP_BASE_URL="https://api.holysheep.ai/v1"

💡 ヒント:.envファイルはプロジェクトのルートフォルダに配置してください。1つ上のフォルダや別名は認識されません。

エラー2:ECONNREFUSED - サーバーに接続できない

Error: connect ECONNREFUSED 127.0.0.1:1234

原因:Pact Mockサーバーが起動していない、またはポート番号が間違っています。

// 解決方法:Pact設定のポートを確認
const provider = new Pact({
  // ...
  port: 1234,  // この番号をMockサーバーへのリクエストと合わせる
});

// 別のポートを使用する場合
const provider = new Pact({
  port: 4000,  // 変更
  // ...
});

// リクエスト先のポートも変更
const response = await axios.post(
  'http://localhost:4000/v1/chat/completions',  // ポート変更
  // ...
);

エラー3:Contract不一致 - レスポンス形式が違う

Expected usage.prompt_tokens to equal "matching(\d+)" but got 12
Response body does not match expectation

原因:Contractで定義した期待値と実際のレスポンスが一致しません。

// 解決方法:まず実際のレスポンスを確認
const response = await axios.post(
  'https://api.holysheep.ai/v1/chat/completions',
  { model: 'gpt-4o', messages: [{ role: 'user', content: 'Test' }] },
  { headers: { 'Authorization': Bearer ${process.env.HOLYSHEEP_API_KEY} } }
);
console.log(JSON.stringify(response.data, null, 2));

// Contractの期待値を実際のレスポンスに合わせて修正
willRespondWith: {
  body: {
    // 正規表現ではなく実際の型に合わせる
    usage: {
      prompt_tokens: 12,      // 数値として検証
      completion_tokens: 5,
      total_tokens: 17
    }
  }
}

エラー4:モデル名が無効

Error: 400 Bad Request
{ "error": { "message": "Invalid value 'gpt-5' for model parameter" } }

原因:存在しないモデル名を指定しています。

# 解決方法:利用可能なモデル一覧を確認
curl https://api.holysheep.ai/v1/models \
  -H "Authorization: Bearer ${HOLYSHEEP_API_KEY}"

利用可能な主要モデル:

- gpt-4o, gpt-4o-mini, gpt-4-turbo

- claude-sonnet-4-20250514, claude-3-5-sonnet-latest

- gemini-2.0-flash, gemini-2.5-flash-preview-05-20

- deepseek-v3-20250617

エラー5:ネットワーク遅延でテストがタイムアウト

Error: Timeout of 2000ms exceeded

原因:デフォルトのタイムアウトが短すぎます。HolySheep AIは低レイテンシですが、ネットワーク状況により変動ことがあります。

// 解決方法:タイムアウト時間を延長
it('正常系: レスポンスが返る', async () => {
  await provider.addInteraction({
    // ...
  });

  const response = await axios.post(
    'http://localhost:1234/v1/chat/completions',
    { /* ... */ },
    {
      timeout: 30000,  // 30秒に延長
      headers: { /* ... */ }
    }
  );
});

// またはmochaのタイムアウトを設定
describe('Chat Completions API', function() {
  this.timeout(30000);  // describeブロック全体に適用
  
  it('正常系: レスポンスが返る', async () => {
    // ...
  });
});

次のステップ:の本番環境での活用

basics掌握了ところで、以下のステップに進んでみましょう。

Contract Testingを導入することで、「APIを変更しても壊れない」という自信がついてきます。私はこの仕組みを知り、怖かったモデル切り替えが楽しくなる转变を経験しました。

まずは小さなContractから始めて、あなたのプロジェクトの守り之神として育ててみてください。


AI 서비스 변경 관리의 첫걸음 —— 지금 바로 시작하세요。

👉 HolySheep AI に登録して無料クレジットを獲得