mcpField
A
api-testing-patterns

api-testing-patterns

github.com/proffesor-for-testingテスト・品質519606 回インストール今日更新
リポジトリ

api-testing-patterns について

api-testing-patternsは、テスト担当者やAPI開発チーム向けの、APIテスト戦略とパターンのガイドです。契約・コンポーネント・統合テストを扱い、利用側の視点から動作やエラー処理を検証します。

主な機能

  1. 01契約・コンポーネント・統合テストの選択
  2. 02利用側の視点でのAPI契約検証
  3. 03認証・入力・エラーケースの確認
  4. 04冪等性と並行処理のテスト
  5. 05OpenAPI仕様からのテスト生成

活用シーン

  1. 01RESTまたはGraphQL APIのテスト戦略設計
  2. 02マイクロサービス間の利用側契約の検証
  3. 03CI/CDでのAPI変更による互換性破壊の防止
  4. 04重要なエンドポイントの負荷特性確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/proffesor-for-testing/agentic-qe --skill api-testing-patterns

インストール後、Claude Code で/api-testing-patternsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

config.json
evals/
api-testing-patterns.yaml
schemas/
output.json
scripts/
validate-config.json
templates/
api-test-scaffold.md

詳細

API テストのパターン

<default_to_action> API をテストする場合、または API テスト戦略を設計する場合:

  1. テストレベルを特定します:コントラクト、統合、コンポーネントのいずれか
  2. 実装ではなくコントラクトをテストします(コンシューマーの視点)
  3. 認証、入力、エラー、冪等性、並行性を検証します
  4. スキーマ検証を使って CI/CD で自動化します
  5. 本番 API を監視してコントラクトのドリフトを検出します

パターンのクイック選択:

  • マイクロサービス → コンシューマー主導のコントラクト(Pact)
  • REST API → CRUD + ページネーション + フィルタリングのテスト
  • GraphQL → クエリの検証 + 複雑度の制限
  • 外部依存関係 → コンポーネントテストでモックを使用
  • パフォーマンス → 重要なエンドポイントの負荷テスト

重要な成功要因:

  • API はコントラクトです。コンシューマーの視点からテストします
  • 正常系だけでなく、必ずエラーシナリオもテストします
  • 破壊的変更を防ぐため、API テストにバージョンを付けます </default_to_action>

クイックリファレンスカード

使用する場面
  • REST または GraphQL API のテスト
  • マイクロサービスのコントラクトの検証
  • API テスト戦略の設計
  • API の破壊的変更の防止
テストレベル
レベル目的依存関係速度
コントラクトプロバイダーとコンシューマー間の合意なし高速
コンポーネント分離した状態の APIモック高速
統合実際の依存関係データベース、サービス低速
重要なテストシナリオ
シナリオ必須テスト例
認証401/403 の処理期限切れのトークン、誤ったユーザー
入力400 の検証必須フィールドの欠落、誤った型
エラー500 の適切な処理DB の停止、タイムアウト
冪等性重複の防止同じ冪等性キー
並行性競合状態並列チェックアウト
ツール
  • コントラクト:Pact、Spring Cloud Contract
  • REST:Supertest、REST-assured、Playwright
  • 負荷:k6、Artillery、JMeter
エージェントの連携
  • qe-api-contract-validator:コントラクトを検証し、破壊的変更を検出します
  • qe-test-generator:OpenAPI 仕様からテストを生成します
  • qe-performance-tester:エンドポイントの負荷テストを実行します
  • qe-security-scanner:API のセキュリティテストを実行します

コントラクトテスト

パターン:コンシューマー主導のコントラクト

// Consumer defines expectations
const contract = {
  request: { method: 'POST', path: '/orders', body: { productId: 'abc', quantity: 2 } },
  response: { status: 201, body: { orderId: 'string', total: 'number' } }
};

// Provider must fulfill
test('order API meets contract', async () => {
  const response = await api.post('/orders', { productId: 'abc', quantity: 2 });

  expect(response.status).toBe(201);
  expect(response.body).toMatchSchema({
    orderId: expect.any(String),
    total: expect.any(Number)
  });
});

**使用場面:**マイクロサービス、分散システム、サードパーティとの統合


重要なテストパターン

認証と認可
describe('Auth', () => {
  it('rejects without token', async () => {
    expect((await api.get('/orders')).status).toBe(401);
  });

  it('rejects expired token', async () => {
    const expired = generateExpiredToken();
    expect((await api.get('/orders', { headers: { Authorization: `Bearer ${expired}` } })).status).toBe(401);
  });

  it('blocks cross-user access', async () => {
    const userAToken = generateToken({ userId: 'A' });
    expect((await api.get('/orders/user-B-order', { headers: { Authorization: `Bearer ${userAToken}` } })).status).toBe(403);
  });
});
入力検証
describe('Validation', () => {
  it('validates required fields', async () => {
    const response = await api.post('/orders', { quantity: 2 }); // Missing productId
    expect(response.status).toBe(400);
    expect(response.body.errors).toContain('productId is required');
  });

  it('validates types', async () => {
    expect((await api.post('/orders', { productId: 'abc', quantity: 'two' })).status).toBe(400);
  });

  it('validates ranges', async () => {
    expect((await api.post('/orders', { productId: 'abc', quantity: -5 })).status).toBe(400);
  });
});
冪等性
it('prevents duplicates with idempotency key', async () => {
  const key = 'unique-123';
  const data = { productId: 'abc', quantity: 2 };

  const r1 = await api.post('/orders', data, { headers: { 'Idempotency-Key': key } });
  const r2 = await api.post('/orders', data, { headers: { 'Idempotency-Key': key } });

  expect(r1.body.orderId).toBe(r2.body.orderId); // Same order
});
並行性
it('handles race condition on inventory', async () => {
  const promises = Array(10).fill().map(() =>
    api.post('/orders', { productId: 'abc', quantity: 1 })
  );
  const responses = await Promise.all(promises);
  const successful = responses.filter(r => r.status === 201);

  const inventory = await db.inventory.findById('abc');
  expect(inventory.quantity).toBe(initialQuantity - successful.length);
});

REST CRUD パターン

describe('Product CRUD', () => {
  let productId;

  it('CREATE', async () => {
    const r = await api.post('/products', { name: 'Widget', price: 10 });
    expect(r.status).toBe(201);
    productId = r.body.id;
  });

  it('READ', async () => {
    const r = await api.get(`/products/${productId}`);
    expect(r.body.name).toBe('Widget');
  });

  it('UPDATE', async () => {
    const r = await api.put(`/products/${productId}`, { price: 12 });
    expect(r.body.price).toBe(12);
  });

  it('DELETE', async () => {
    expect((await api.delete(`/products/${productId}`)).status).toBe(204);
    expect((await api.get(`/products/${productId}`)).status).toBe(404);
  });
});

ベストプラクティス

✅ 実施すること
  • コンシューマーの視点からテストする
  • スキーマ検証を使用する(完全一致する値ではなく)
  • エラーシナリオを徹底的にテストする
  • API テストにバージョンを付ける
  • CI/CD で自動化する
❌ 避けること
  • コントラクトではなく実装をテストする
  • HTTP のセマンティクス(ステータスコード)を無視する
  • ネガティブテストを行わない
  • フィールドの順序や余分なフィールドをアサートする
  • テストが遅くなること(外部サービスをモックする)

エージェント支援による API テスト

// Validate contracts
await Task("Contract Validation", {
  spec: 'openapi.yaml',
  endpoint: '/orders',
  checkBreakingChanges: true
}, "qe-api-contract-validator");

// Generate tests from spec
await Task("Generate API Tests", {
  spec: 'openapi.yaml',
  coverage: 'comprehensive',
  include: ['happy-paths', 'input-validation', 'auth-scenarios', 'error-handling']
}, "qe-test-generator");

// Load test
await Task("API Load Test", {
  endpoint: '/orders',
  rps: 1000,
  duration: '5min'
}, "qe-performance-tester");

// Security scan
await Task("API Security Scan", {
  spec: 'openapi.yaml',
  checks: ['sql-injection', 'xss', 'broken-auth', 'rate-limiting']
}, "qe-security-scanner");

エージェント連携のヒント

メモリの名前空間
aqe/api-testing/
├── contracts/*        - API contract definitions
├── generated-tests/*  - Generated test suites
├── validation/*       - Contract validation results
└── performance/*      - Load test results
フリート連携
const apiFleet = await FleetManager.coordinate({
  strategy: 'contract-testing',
  agents: ['qe-api-contract-validator', 'qe-test-generator', 'qe-test-executor'],
  topology: 'mesh'
});

await apiFleet.execute({
  services: [
    { name: 'orders-api', consumers: ['checkout-ui', 'admin-api'] },
    { name: 'payment-api', consumers: ['orders-api'] }
  ]
});

関連スキル


覚えておくこと

API テストとは、実装ではなくコントラクトと動作を検証することです。正しいレスポンス、適切なエラー処理、許容できるパフォーマンスなど、コンシューマーにとって重要な点に焦点を当てます。

**エージェントを活用する場合:**エージェントはコントラクトの検証を自動化し、仕様から包括的なテストスイートを生成し、本番 API のドリフトを監視します。大規模な API 品質の維持にエージェントを活用してください。

注意点

  • エージェントは実際の API ではなく、ドキュメントに記載された API に対するテストを生成します。まず実行中のサービスで必ず検証してください
  • 認証トークンはテスト実行の間に期限切れになります。有効期間の長いトークンを使うフィクスチャを使用するか、各テストスイートの実行前に更新してください
  • CI でのレート制限により、断続的な失敗が発生します。429 レスポンスに対して指数バックオフを伴うリトライを追加してください
  • GraphQL のイントロスペクションは本番環境で無効になっている場合があります。本番エンドポイントではなく、ステージング環境のスキーマに対してテストしてください
  • 冪等性のテストでは、実行ごとに一意のリクエスト ID が必要です。ID をハードコードすると、リトライ時に誤ってテストが成功することがあります

コメント