mcpField
D
drift-testing

drift-testing

github.com/pactflowテスト・品質7100 回インストール2 日前に更新
リポジトリ

drift-testing について

Drift Skillは、OpenAPIおよびAsyncAPI仕様に基づくDriftのAPIコントラクトテストを案内します。APIテストを作成・保守するチーム向けに、テスト雛形、カバレッジ確認、検証ループのスクリプトと、OpenAPI用のPrismローカルモックを提供します。

主な機能

  1. 01OpenAPI・AsyncAPI仕様からテスト雛形を生成
  2. 02操作とレスポンスコードのカバレッジ確認
  3. 03検証とカバレッジのフィードバックループ実行
  4. 04OpenAPI用Prismモックサーバーの起動
  5. 05認証、フック、CLIのリファレンス

活用シーン

  1. 01未カバーの仕様操作にテストを作成
  2. 02OpenAPI仕様に照らしてREST APIを検証
  3. 03AsyncAPI 3.xのイベント駆動サービスをテスト
  4. 04Prismモックを使ったローカルAPI検証

インストール

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

$npx skills add https://github.com/pactflow/pactflow-agent-skills --skill drift-testing

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

同梱ファイル

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

evals/
01-basic-get-endpoint.json
02-stateful-delete-endpoint.json
03-debug-failing-test.json
evals/fixtures/
products-api-with-failures.md
simple-products-api.yaml
references/
auth.md
cli-reference.md
lua-api.md
mock-server.md
pactflow-and-cicd.md
test-cases.md
scripts/
asyncapi_targets.py
check_coverage.py
extract_channels.py
extract_endpoints.py
run_loop.ps1
run_loop.sh
start_mock.ps1
start_mock.sh
scripts/tests/
test_asyncapi_support.py

詳細

Drift スキル

テスト対象の OpenAPI 仕様を変更しないでください。

参照ファイル

  • references/test-cases.md — テストケースの YAML スキーマ全体、すべてのパターン、データセット、式
  • references/auth.md — 認証設定、動的トークン、標準以外のスキーム、401/403 のテスト
  • references/mock-server.md — Prism を使ったローカルテスト:セットアップ、Prefer ヘッダー、仕様の品質に関する問題
  • references/lua-api.md — Lua API の全機能:ライフサイクルイベント、http()、dbg()、エクスポートされる関数
  • references/cli-reference.md — すべての CLI コマンド/フラグ、設定、並列実行、終了コード
  • references/pactflow-and-cicd.md — BDCT の公開ワークフロー、GitHub Actions、GitLab CI

スクリプト

  • scripts/extract_endpoints.py — 仕様を読み取り、すべてのオペレーションとレスポンスコードを出力します。 サマリーモードでは、仕様内に例がないパラメーターにフラグを付けます。スキャフォールドモード(--scaffold)では、認証パターン、404 用の nil UUID、4xx 用の ignore.schema、FILL_IN マーカーを適切に設定した、すぐに記入できる operations: YAML ブロックを出力します。既存のテストファイルでまだカバーされていない項目のみを生成するには、--only-missing <drift.yaml> を使用します。pyyaml が必要です。
  • scripts/extract_channels.py — extract_endpoints.py の AsyncAPI 版です。AsyncAPI 3.x の仕様を読み取り、すべてのオペレーションと、そのアクションタイプ(send/receive)、実行モード(async-observe/async-inject/async-request-reply)、チャネルアドレスを出力します。スキャフォールドモード(--scaffold)では、モードに応じた正しいトリガー/プローブ構造を備えた、すぐに記入できる Drift の operations: YAML スタブを出力します。まだカバーされていないオペレーションのみをスキャフォールドするには、--only-missing <drift.yaml> を使用します。pyyaml が必要です。AsyncAPI 3.x のみ対応しています。
  • scripts/check_coverage.py — カバレッジチェッカー:OpenAPI または AsyncAPI の仕様と Drift のテストファイルを比較します。 OpenAPI のカバレッジではオペレーションとレスポンスコードを追跡し、AsyncAPI のカバレッジでは各オペレーション/メッセージの対象を追跡します。 pyyaml が必要です。
  • scripts/run_loop.sh / scripts/run_loop.ps1 — フィードバックループランナー:すべてのテストがパスするまで drift verify --failed を再試行し、その後 check_coverage.py を実行します。 終了コードが 0 になるには、両方のゲートを通過する必要があります。依存関係は uv を使って自動的にインストールされます。 Windows では .ps1 版を使用してください。
  • scripts/start_mock.sh / scripts/start_mock.ps1 — OpenAPI 仕様から Prism モックサーバーを起動します。必要に応じて Prism をインストールします。 --port および --dynamic フラグに対応しています。Windows では .ps1 版を使用してください。

ドキュメント全文:https://support.smartbear.com/swagger/contract-testing/docs/en/drift.html 利用可能なページをすべて確認するには、サイトマップを取得してください:https://support.smartbear.com/swagger/contract-testing/docs/sitemap.xml


インストール

# Quickest — no install needed
npx @pactflow/drift --help

# Project-level (recommended for teams)
npm install --save-dev @pactflow/drift

# Global
npm install -g @pactflow/drift

# Verify
drift --version

プロジェクトのセットアップ

drift init   # interactive wizard — scaffolds all files below

drift init は対話式です。ユーザーに実行してもらってください。

drift/
├── drift.yaml              # Main config — sources, plugins, global settings
├── drift.lua               # Lifecycle hooks and helper functions
├── my-api.dataset.yaml     # Test data
└── my-api.tests.yaml       # Test cases

最小限の drift.yaml:

# yaml-language-server: $schema=https://download.pactflow.io/drift/schemas/drift.testcases.v1.schema.json
drift-testcase-file: v1
title: "My API Tests"

sources:
  - name: source-oas # referenced in test targets
    path: ./openapi.yaml # or uri: https://... for remote specs
  - name: product-data
    path: ./product.dataset.yaml
  - name: functions
    path: ./product.lua

plugins:
  - name: oas # spec-first verification
  - name: json
  - name: data

global:
  auth:
    apply: true
    parameters:
      authentication:
        scheme: bearer # bearer | basic | api-key
        token: ${env:API_TOKEN}

operations:
  # test cases here — see references/test-cases.md

テストの実行

# Basic run
drift verify --test-files drift.yaml --server-url https://api.example.com/v1

# Single operation (fast iteration)
drift verify --test-files drift.yaml --server-url https://api.example.com/v1 --operation getProductByID

# Re-run only failures
drift verify --test-files drift.yaml --server-url https://api.example.com/v1 --failed

# Filter by tags
drift verify --test-files drift.yaml --server-url https://api.example.com/v1 --tags smoke
drift verify --test-files drift.yaml --server-url https://api.example.com/v1 --tags '!destructive'

すべてのフラグ、並列実行、JUnit 出力、終了コードについては、references/cli-reference.md を参照してください。


カバレッジ全体のフィードバックループ

エンドポイントの完全なカバレッジが目標の場合:

注意 — 本番環境での破壊的なテスト: --server-url が稼働中の 本番 API を指している場合、DELETE および POST テストは取り消せません。必ず専用のテストアカウントを使用し、 DELETE テストで使用するリソースが破棄可能であることを確認してください。

このチェックリストをコピーして、進捗を記録してください:

Coverage Loop Progress:
- [ ] Step 0: Check current coverage (check_coverage.py)
- [ ] Step 1: Parse spec and collect operation list (openapi-parser skill or extract_endpoints.py)
- [ ] Step 2: Assemble initial test file
- [ ] Step 3: Run tests (run_loop.sh / run_loop.ps1)
- [ ] Step 4: Diagnose and fix each failure
- [ ] Step 5: Apply common fixes (hooks for state, data seeding)
- [ ] Step 6: Verify exit code 0 + full coverage
ステップ 0 — 現在のカバレッジを確認する

テストを作成する前、または既存のテストスイートを再開する際に実行してください:

# Run against your spec and test file(s)
uv run path/to/scripts/check_coverage.py \
  --spec openapi.yaml \
  --test-files drift.yaml

# Multiple files / globs
uv run path/to/scripts/check_coverage.py \
  --spec openapi.yaml \
  --test-files "tests/*.yaml"

# Machine-readable output (for CI or scripting)
uv run path/to/scripts/check_coverage.py \
  --spec openapi.yaml \
  --test-files drift.yaml --json

出力には、テストがまったくないオペレーション、特定のレスポンスコードが未対応のオペレーション、オペレーション/コード全体の割合が表示されます。終了コード 0 = カバレッジ完全、1 = 未対応の項目あり。

このスクリプトでは、デフォルトで 500/501/502/503 を除外します(下記のステップ 1 と同じルールです)。除外するコードを変更するには、--exclude-codes を渡してください。

ステップ 1 — 仕様を解析する

extract_endpoints.py を使って、操作の完全な一覧、操作ごとに文書化されたすべてのレスポンスコード、およびすぐに使える operations: YAML スタブを収集します。お使いの環境で openapi-parser スキルが利用できる場合は、代わりにそちらを使用できます。

# See all operations + response codes, flagging params with no spec example
uv run scripts/extract_endpoints.py --spec openapi.yaml

# Generate skeleton stubs for every operation
uv run scripts/extract_endpoints.py --spec openapi.yaml \
  --scaffold --source my-oas > operations.yaml

# Generate ONLY the gaps not already in an existing test file
uv run scripts/extract_endpoints.py --spec openapi.yaml \
  --scaffold --only-missing drift.yaml --source my-oas >> drift.yaml
GET /products          → 200, 401, 404
POST /products         → 201, 400, 401
DELETE /products/{id}  → 204, 401, 403, 404

重要: 仕様レベルの example がないパラメーターがあると、Value for query parameter X is missing が発生します。それぞれに parameters.query/path/headers で明示的な値を指定してください。

全体で必須のクエリパラメーター(たとえば、すべてのエンドポイントでの ?version=YYYY-MM-DD)は、各テストケースで繰り返す代わりに、http:request フックを使って一度だけ挿入できます。

["http:request"] = function(event, data)
  if data.query == nil then data.query = {} end
  data.query["version"] = "2024-01-04"
  return data   -- MUST return modified data
end

重複する operationId 値 — 一部の仕様では、異なる 2 つのパスに同じ operationId が再利用されています。該当する操作には method:path による指定を使用してください。

target: source-oas:post:/orgs/{org_id}/apps/installs/{install_id}/secrets

500 レスポンスはカバレッジ要件の対象外です — 500 はサーバーのバグが必要であり、決定論的に発生させることはできません。

ステップ 2 — 初期テストファイルを作成する

openapi-parser のスタブを drift.yaml に組み込みます。最初から完璧を目指す必要はありません。反復することで不足箇所が明らかになります。各テストはできるだけシンプルな状態から始めてください。

getProduct_Success:
  target: source-oas:getProductByID
  parameters:
    path:
      id: 10
  expected:
    response:
      statusCode: 200

すべての操作にタグを追加します。タグを使うと --tags でフィルタリングでき、テストスイートも管理しやすくなります。

getProduct_Success:
  target: source-oas:getProductByID
  tags: [smoke, read-only, products]
  ...

getProduct_Unauthorized:
  tags: [security, auth]
  ...

deleteProduct_Success:
  tags: [destructive, products]
  ...

一般的なタグ:smoke、read-only、write、destructive、security、auth、regression。タグの詳細については、references/test-cases.md のタグのセクションを参照してください。

エラーパスについては、401、403、404、400 のパターンを references/test-cases.md で確認してください。 モックサーバーのセットアップについては、references/mock-server.md を参照してください。

ステップ 3 — 実行して失敗を記録する

run_loop.sh スクリプトは、ステップ 6 までのこの全工程を自動化します。

# Runs drift --failed in a loop, then checks coverage. Exits 0 only when both pass.
path/to/scripts/run_loop.sh \
  --spec openapi.yaml \
  --test-files drift.yaml \
  --server-url https://api.example.com/v1

または、drift を手動で実行して反復します。

drift verify --test-files drift.yaml --server-url https://api.example.com/v1

# Re-run only failures to keep the loop fast
drift verify --test-files drift.yaml --server-url https://api.example.com/v1 --failed

モックサーバーを使ってローカルでテストする場合は、先に Prism を起動してください。

path/to/scripts/start_mock.sh --spec openapi.yaml --port 4010
# then in another terminal:
path/to/scripts/run_loop.sh --spec openapi.yaml --test-files drift.yaml --server-url http://localhost:4010
ステップ 4 — 各失敗の原因を特定して修正する
症状考えられる原因修正
404 が返り、200 が期待されていたテストデータが存在しないリソースをシードするために operation:started フックを追加します
200 が返り、404 が期待されていたID が偶然存在している${notIn(...)} または nil UUID 00000000-0000-0000-0000-000000000000 を使います
401 が返り、200 が期待されていた認証が設定されていないglobal.auth を追加するか、トークンの環境変数を確認します
200 が返り、401 が期待されていた認証情報が除去されていないexclude: [auth] と不正なトークンを追加します
403 が返り、200 が期待されていたトークンに必要なスコープがない十分な権限を持つトークンを使います
200 が返り、403 が期待されていた有効な認証情報とアクセス禁止のリソースが必要トークンでアクセスできないリソースを指定します。references/auth.md を参照してください
レスポンスのスキーマ検証エラーAPI が仕様から逸脱している、または仕様の例が無効仕様の例が有効か確認します。Drift が仕様のバグを正しく報告している可能性があります
Value for query parameter X is missing任意のパラメーターに仕様の example がない仕様に例がないパラメーターにはすべて明示的な値を指定します
200 のテストで 400 が返るグローバルに必須のクエリパラメーターが不足しているhttp:request フック経由で挿入するか、各テストケースに追加します
500 が返るテストデータがサーバーのバグを引き起こしたデータを修正します

ignore: { schema: true } が抑制するのはリクエストのスキーマ検証のみです。4xx のシナリオではこれを使用してください。特にモックサーバーに対するテストでは、Prism は認証を適用せず、不正確なエラーボディを返すことがあります。レスポンスのスキーマ検証を回避する方法はありません。仕様の例にあるバグは失敗として表面化します(references/mock-server.md を参照してください)。

複数の 2xx コード: ドキュメントに記載されたコードごとにテストを 1 つ作成してください。statusCode: [200, 204] の配列構文はサポートされていません。

動的 ID とフックのタイミング: データセットの式は operation:started より前に解決されます。事前にシードした静的 ID を使用するか、http:request 経由で URL を書き換えてください。

ステップ 5 — よくある修正

テストの前にデータが存在している必要があります(DELETE、PUT、PATCH):

["operation:started"] = function(event, data)
  if data.operation == "deleteProduct_Success" then
    http({ url = server_url .. "/products", method = "POST",
           body = { id = 10, name = "test", price = 9.99 } })
  end
end,
["operation:finished"] = function(event, data)
  http({ url = server_url .. "/products/10", method = "DELETE" })
end,

Lua API 全体と data オブジェクトの構造については、references/lua-api.md を参照してください。

ステップ 6 — 終了コード 0 と完全なカバレッジを確認する
drift verify --test-files drift.yaml --server-url https://api.example.com/v1
echo "Exit code: $?"

完了と判断する前に、カバレッジが完全であることを確認してください。

uv run path/to/scripts/check_coverage.py \
  --spec openapi.yaml --test-files drift.yaml
echo "Coverage exit: $?"

両方のコマンドが終了コード 0 で終了したら完了です。


AsyncAPI テスト

AsyncAPI テストでは、AsyncAPI 3.x のドキュメントに照らして、イベント駆動型サービス(Kafka、SNS、SQS)を検証します。実際に稼働しているブローカーが必要です。これに相当するモックサーバーはありません。

サポート対象バージョン: AsyncAPI 3.x のみです。Drift は AsyncAPI 2.x をサポートしていません。

プラグインスタック(すべての AsyncAPI テストファイルで必須)
# yaml-language-server: $schema=https://download.pactflow.io/drift/schemas/drift.testcases.v1.schema.json
drift-testcase-file: v1
title: "<service> AsyncAPI verification"

sources:
  - name: <source-name>               # referenced in target fields
    path: ./asyncapi/<service>.asyncapi.yaml

plugins:
  - name: asyncapi      # reads the AsyncAPI document
  - name: kafka         # transport; use aws-messaging for SNS/SQS
  - name: json          # payload validation

operations:
  # test cases go here
AsyncAPI の完全なカバレッジ・フィードバックループ
AsyncAPI Coverage Loop Progress:
- [ ] Step 0: Check current coverage (check_coverage.py auto-detects AsyncAPI)
- [ ] Step 1: Parse spec and collect operation list (asyncapi-parser skill or extract_channels.py)
- [ ] Step 2: Assemble test file with correct plugins and sources
- [ ] Step 3: Start broker (Kafka/SNS/SQS) and run drift verify
- [ ] Step 4: Diagnose and fix failures
- [ ] Step 5: Verify full coverage
ステップ 0 — 現在の AsyncAPI カバレッジを確認する

check_coverage.py は AsyncAPI の仕様を自動検出し、操作/メッセージレベルのカバレッジ(ステータスコードなし)に切り替えます。 修飾子のない <source>:<operationId> ターゲットは互換性のために操作全体をカバーします。複数のメッセージを正確にカバーするには <source>:<operationId>:<messageId> を使用してください。

uv run path/to/scripts/check_coverage.py \
  --spec service.asyncapi.yaml \
  --test-files "tests/*.yaml"
# Exit 0 = all operation/message targets have at least one test; 1 = gaps remain
ステップ 1 — 仕様を解析する

extract_channels.py を使ってすべての操作を確認し、ひな形のスタブを生成します。複雑なメッセージスキーマ(ペイロード内の oneOf/anyOf/allOf/discriminator)については、代わりに asyncapi-parser スキルを呼び出します。

# Summary: see all operations + execution modes
uv run scripts/extract_channels.py --spec service.asyncapi.yaml

# Scaffold stubs for all operations
uv run scripts/extract_channels.py --spec service.asyncapi.yaml \
  --scaffold --source async-svc > drift/tests.yaml

# Scaffold only gaps in an existing test file
uv run scripts/extract_channels.py --spec service.asyncapi.yaml \
  --scaffold --only-missing drift/tests.yaml --fragments >> drift/tests.yaml

--fragments を指定すると、ルートの operations: キーが省略されるため、追記しても YAML マッピングが重複しません。 指定しない場合、生成されるひな形は完全な独立した operations: ブロックとなり、新しいファイルへのリダイレクトを想定しています。

ステップ 2 — テストファイルを組み立てる

ひな形のスタブに、正しい plugins/sources ヘッダーを設定します。各操作には次の項目が必要です。

  • parameters の correlation-id — Drift はこれを使って該当するメッセージを照合します
  • timeout-ms — メッセージを待つ時間(デフォルト:5000ms)
  • async-observe には trigger フック、async-inject、async-inject-capture、async-request-reply には payload
  • 受信操作には probe フック(async-inject)または probe-topic(async-inject-capture)

trigger フックについて:trigger コマンドは、${parameters.correlation-id} を公開するメッセージのヘッダーに必ず引き継ぐ必要があります。correlation ID が一致しないと、キャプチャがタイムアウトします。

ステップ 3 — テストを実行する

AsyncAPI テストでは --server-url は不要です。Drift は AsyncAPI の servers ブロックで宣言されたブローカーに接続します。

# Run all tests
drift verify --test-files drift/tests.yaml

# Re-run only failures
drift verify --test-files drift/tests.yaml --failed

# Single operation (fast iteration)
drift verify --test-files drift/tests.yaml --operation SendOrderCreated_Observe

注記: run_loop.sh は OpenAPI 用に設計されています(--server-url が必要です)。AsyncAPI では、すべてのテストが通るまで drift verify --failed を使って手動で繰り返し実行し、その後 check_coverage.py を実行してカバレッジを確認します。

ステップ 4 — AsyncAPI の失敗を診断する
症状考えられる原因対処法
キャプチャがタイムアウトし、trigger は成功したtrigger が別の correlation ID を公開したtrigger が ${parameters.correlation-id} をヘッダー値として渡していることを確認します
キャプチャがタイムアウトし、trigger が実行されなかったtrigger スクリプトへのパスが間違っているテストケースの YAML ファイルの場所を基準にしたパスを使います
キャプチャがタイムアウトし、trigger が関与していないchannel address が間違っているか、ブローカーが起動していないAsyncAPI 仕様の address: が実際のトピック名と一致することを確認します
probe が誤検知する(通るべきでないテストが通る)前のテストの状態がクリアされていないprobe の開始時に状態をクリアするロジックを追加します
probe が断続的に失敗するサービスの処理完了前に probe が実行されるprobe スクリプトに再試行ループまたは遅延を追加します
公開時のスキーマ検証に失敗するペイロードに必須フィールドがないcomponents.messages.<name>.payload.required を確認します
request-reply が想定されるのに async-observe モードになっているAsyncAPI 操作に reply ブロックがないparameters に reply-channel: <topic> を追加するか、AsyncAPI 仕様を修正します
SNS/SQS:request-reply が動作しないSNS/SQS は request-reply をサポートしていないrequest-reply のやり取りには Kafka を使います
開発用ローカル Kafka

Docker でローカルブローカーを起動します。

# Start Kafka (KRaft mode, no Zookeeper)
docker run -d --name drift-kafka -p 9092:9092 \
  -e KAFKA_ENABLE_KRAFT=yes \
  -e KAFKA_NODE_ID=1 \
  -e KAFKA_PROCESS_ROLES=broker,controller \
  -e KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER \
  -e KAFKA_LISTENERS="PLAINTEXT://:9092,CONTROLLER://:9093" \
  -e KAFKA_ADVERTISED_LISTENERS="PLAINTEXT://localhost:9092" \
  -e KAFKA_LISTENER_SECURITY_PROTOCOL_MAP="PLAINTEXT:PLAINTEXT,CONTROLLER:PLAINTEXT" \
  -e KAFKA_CONTROLLER_QUORUM_VOTERS="1@localhost:9093" \
  -e KAFKA_AUTO_CREATE_TOPICS_ENABLE=true \
  -e KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1 \
  confluentinc/cp-kafka:latest

# Stop when done
docker stop drift-kafka && docker rm drift-kafka

AsyncAPI の servers ブロックでブローカーへの接続を宣言します。プラグイン設定で上書きしない限り、Drift はそこに記載された接続を使います。


クイックリファレンス

シナリオ方法
ステートレスな読み取り専用エンドポイント宣言型テスト、フックなし
安定したテストデータデータセット式
テスト前にデータを作成するoperation:started フック
テスト後にクリーンアップするoperation:finished フック
動的な値(UUID、タイムスタンプ)Lua の exported_functions
404 を確実に返す${notIn(...)} または nil UUID
モックサーバーでエラーコードを強制するPrefer: code=X ヘッダー
ライブバックエンドなしでテストするPrism モック — references/mock-server.md を参照
標準外の認証プレフィックスhttp:request フック — references/auth.md を参照
失敗したテストのみを再実行する--failed フラグ
PactFlow に公開する--generate-result フラグ
AsyncAPI の送信操作(監視)async-observe モード — trigger フックを追加
AsyncAPI の受信操作(注入)async-inject モード — probe フックを追加するか、probe-topic を使用
AsyncAPI のリクエスト/リプライasync-request-reply モード — trigger は不要
複雑な AsyncAPI メッセージスキーマを解析するasyncapi-parser スキル
AsyncAPI テストスタブを生成するextract_channels.py --scaffold
AsyncAPI のカバレッジを確認するcheck_coverage.py(仕様の種類を自動検出)

コメント