api-contract-testing について
API が利用側と提供側の合意に沿っているかを確認するテストスキルです。Pact によるコンシューマー駆動テスト、提供側の検証、OpenAPI スキーマ検証を扱います。
主な機能
- 01Pact によるコンシューマー駆動テスト
- 02提供側の検証
- 03OpenAPI のリクエストとレスポンス検証
- 04複数言語エコシステムのガイド
活用シーン
- 01依存サービス間の API 合意の確認
- 02公開済み契約に対する提供側の動作検証
- 03OpenAPI による Express の入出力検証
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ secondsky/ claude-skills --skill api-contract-testingインストール後、Claude Code で/api-contract-testingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/java-rest-assured.mdpact-broker-cicd.mdpython-json-schema.md詳細
API コントラクトテスト
完全な統合テストを実施せずに、API がコンシューマーとプロバイダー間のコントラクトに従っていることを検証します。
主な概念
| 用語 | 定義 |
|---|---|
| コンシューマー | API を呼び出すサービス |
| プロバイダー | API を公開するサービス |
| コントラクト | 合意されたリクエスト/レスポンス形式 |
| Pact | コンシューマー主導のコントラクトテストツール |
| スキーマ | 構造の定義(OpenAPI、JSON Schema) |
| ブローカー | コントラクトの中央リポジトリ |
Pact コンシューマーテスト(TypeScript)
import { PactV3, MatchersV3 } from '@pact-foundation/pact';
const provider = new PactV3({
consumer: 'OrderService',
provider: 'UserService'
});
describe('User API Contract', () => {
it('returns user by ID', async () => {
await provider
.given('user 123 exists')
.uponReceiving('a request for user 123')
.withRequest({ method: 'GET', path: '/users/123' })
.willRespondWith({
status: 200,
body: MatchersV3.like({
id: '123',
name: MatchersV3.string('John'),
email: MatchersV3.email('[email protected]')
})
})
.executeTest(async (mockServer) => {
const response = await fetch(`${mockServer.url}/users/123`);
expect(response.status).toBe(200);
});
});
it('returns 404 for non-existent user', async () => {
await provider
.given('user does not exist')
.uponReceiving('a request for non-existent user')
.withRequest({ method: 'GET', path: '/users/999' })
.willRespondWith({
status: 404,
body: MatchersV3.like({
error: { code: 'NOT_FOUND', message: MatchersV3.string() }
})
})
.executeTest(async (mockServer) => {
const response = await fetch(`${mockServer.url}/users/999`);
expect(response.status).toBe(404);
});
});
});
プロバイダーの検証
import { Verifier } from '@pact-foundation/pact';
new Verifier({
provider: 'UserService',
providerBaseUrl: 'http://localhost:3000',
pactBrokerUrl: process.env.PACT_BROKER_URL,
publishVerificationResult: true,
providerVersion: process.env.GIT_SHA,
stateHandlers: {
'user 123 exists': async () => {
await db.users.create({ id: '123', name: 'John' });
},
'user does not exist': async () => {
await db.users.deleteAll();
}
}
}).verifyProvider();
OpenAPI の検証(Express)
const OpenApiValidator = require('express-openapi-validator');
app.use(OpenApiValidator.middleware({
apiSpec: './openapi.yaml',
validateRequests: true,
validateResponses: true,
validateSecurity: true
}));
その他の実装
- Python JSON Schema:references/python-json-schema.mdを参照してください
- Java REST Assured:references/java-rest-assured.mdを参照してください
- Pact Broker CI/CD:references/pact-broker-cicd.mdを参照してください
ベストプラクティス
実施すること:
- コンシューマーの視点からテストする
- 柔軟な照合にはマッチャーを使用する
- 特定の値ではなく、構造を検証する
- コントラクトのバージョンを明示する
- エラーレスポンスをテストする
- CI パイプラインでテストを実行する
- 後方互換性をテストする
実施しないこと:
- コントラクトでビジネスロジックをテストする
- 特定の値をハードコードする
- エラーシナリオを省略する
- バージョン管理を無視する
- 検証せずにデプロイする
ツール
- Pact - 複数言語に対応したコンシューマー主導のコントラクト
- Spring Cloud Contract - JVM エコシステム
- OpenAPI/Swagger - スキーマファーストの検証
- Dredd - API Blueprint のテスト
- Spectral - OpenAPI のリンティング
コメント
Markdown 対応