| name | test-from-contract |
| description | Generate TDD tests from Contract YAML files. Use when the user wants to "generate tests from contracts", "create TDD tests", "test from contract", "generate test stubs", "create Level 1 and Level 2 tests", "write contract tests", "TDD from contracts", "generate UI tests", "create screen tests", "test UI components from contract", or "create frontend test stubs". Also use when the user says "テストを生成する", "コントラクトからテスト作成", "TDDテストを書く", "テストスタブを生成", or "コントラクトをテストする". Reads .blueprint/contracts/ and produces Level 1 (structure validation) and Level 2 (implementation stubs) test files, plus UI tests in tests/ui/ for screen contracts. |
| version | 1.0.0 |
| core_ref | core/test-from-contract.md |
Test From Contract スキル (Claude Code)
Contract YAML から TDD テストコードを自動生成するスキル。
Level 1(構造検証、即 GREEN)+ Level 2(実装検証、RED スタブ)の 2 レベルでテストを出力する。
仕様参照
本スキルのワークフローは core/test-from-contract.md に定義。
Contract スキーマは core/contract-schema.md を参照。
テスト生成ルールは {baseDir}/references/test-generation-rules.md を参照。
前提条件
| 条件 | 必須 | 説明 |
|---|
.blueprint/contracts/ | ○ | /spec で生成済みの Contract YAML が最低 1 つ必要 |
| Git リポジトリ | ○ | tests/ をプロジェクトルートに配置 |
| テストフレームワーク | △ | 未検出時はユーザーに確認(デフォルト: Vitest) |
出力ファイル
| ディレクトリ | 内容 |
|---|
tests/contracts/level1/ | 構造検証テスト(即 GREEN、全タイプ) |
tests/contracts/level2/ | 実装検証テスト(RED スタブ、api/external/file/internal のみ) |
tests/contracts/helpers/ | 共通ヘルパー(contract-loader, タイプ別ヘルパー) |
tests/ui/{screen-name}/ | UI テスト(RED スタブ、screen タイプのみ) |
tests/e2e/ | E2E テスト(screen + e2e_tool 設定時のみ) |
playwright.config.ts | E2E 設定(e2e_tool: playwright の場合のみ、ルートに生成) |
ツール
| ツール | 用途 |
|---|
| Bash | git root 検出、テストフレームワーク検出 |
| Glob | Contract ファイルスキャン、既存テスト検出、設定ファイル検出 |
| Read | Contract YAML 読み込み、package.json/tsconfig 等の設定読み込み |
| Write | テストファイル書き出し |
ワークフロー(Claude Code 固有部分)
core/test-from-contract.md の 6 ステップに従う。以下は Claude Code 固有の実行詳細:
Step 1: コンテキスト読み込み
git rev-parse --show-toplevel
# Contract ファイルをスキャン
Glob(".blueprint/contracts/**/*.contract.yaml")
各 Contract YAML を Read() して id, type, status, version を取得。
ディレクトリ → タイプマッピング:
contracts/api/ → api
contracts/external/ → external
contracts/file/ → file
contracts/internal/ → internal(subtype でテスト生成パターン分岐)
contracts/screen/ → screen(screen_type でテスト生成パターン分岐、UI テストは tests/ui/ に配置)
Step 2: Contract 選択
一覧をユーザーに提示:
## テスト生成対象の Contract
| # | Contract ID | Type | Status | Version |
|---|---------------------------|----------|--------|---------|
| 1 | CON-order-create | api | draft | 1.0.0 |
| 2 | CON-stripe-payment-intent | external | draft | 1.0.0 |
デフォルト: 全件(active + draft)を対象
対象を変更しますか?
ユーザー承認を得てから Step 3 へ進む。
Step 3: テスト環境確認
# フレームワーク自動検出
Glob("**/vitest.config.*") # → Vitest
Glob("**/jest.config.*") # → Jest
Read("package.json") # jest フィールド確認
Glob("**/conftest.py") # → pytest
Glob("**/*_test.go") # → Go testing
# 既存テストディレクトリ確認
Glob("tests/**/*.test.*")
Glob("__tests__/**/*")
Glob("src/**/*.test.*")
検出結果をユーザーに報告:
## テスト環境
- フレームワーク: Vitest (vitest.config.ts から検出)
- 出力先: tests/contracts/
- 既存テスト: tests/ に 15 ファイル
この設定で生成しますか?
Step 4: Level 1 テスト生成
tests/contracts/helpers/contract-loader.ts を生成(存在しない場合)
- 各 Contract ごとに:
Read() で Contract YAML を読み込み
{baseDir}/references/test-generation-rules.md の Level 1 ルールに従って生成
Write() で tests/contracts/level1/CON-{name}.test.ts に出力
@generated マーカー: 各ファイル先頭に以下を付与:
Step 5: Level 2 テスト生成
- 各 Contract ごとに:
Read() で Contract YAML を読み込み
- 依存 Contract も
Read() で読み込み(links.depends_on がある場合)
{baseDir}/references/test-generation-rules.md の Level 2 ルールに従って生成
- タイプに応じた describe ブロック構成:
- api: Input Validation / Business Rules / State Transitions / Error Handling / Response Validation
- external: Request Construction / Constraints / Error Handling / Response Validation
- file: Input Validation (columns) / File-Level Constraints / Processing Rules / Error Handling / Result Validation
- internal (service): Method Input / Rules / State Lifecycle / Side Effects / Return Values
- internal (repository): CRUD Roundtrip / Not Found Handling / Rules / Overwrite / Storage Specifics
Write() で tests/contracts/level2/CON-{name}.test.ts に出力
- screen Contract の場合は UI テスト(tests/ui/)を生成(Level 2 ではなく別レイヤー):
{baseDir}/references/test-generation-rules.md の「screen 型: UI テスト」ルールに従う
config.yaml の tech_stack.frontend.test_tool でフレームワークを決定
Write() で tests/ui/{screen-name}/{ScreenName}Page.test.tsx に出力
- ヘルパー:
tests/ui/{screen-name}/screen-helpers.ts
- タイプ別ヘルパーを生成(
tests/contracts/helpers/{type}-helpers.ts)
各テストの導出元コメント: テスト内に Contract フィールドへの参照を埋め込む:
it("BR-001: サーバー側で金額再計算", () => {
expect.fail("RED: implement to verify server-side amount recalculation");
});
Step 5.5: E2E テスト生成(条件付き)
# config.yaml の e2e_tool を確認
Read(".blueprint/config.yaml") → tech_stack.frontend.e2e_tool を取得
# e2e_tool が none または frontend セクションなし → スキップ
# e2e_tool が playwright または cypress → 生成開始
# playwright.config.ts の存在確認(存在する場合はスキップ)
Glob("playwright.config.ts")
# 存在しない場合のみ Write で生成
Write("playwright.config.ts", <E2E設定>)
# screen Contract 一覧取得(Step 1 で取得済み)
# 各 screen Contract に対して Write で E2E テストファイルを生成
Write("tests/e2e/{screen-name}.spec.ts", <E2E テスト>)
playwright.config.ts の標準テンプレート (Hono + Vite の場合):
import { defineConfig } from '@playwright/test'
export default defineConfig({
testDir: './tests/e2e',
use: { baseURL: 'http://localhost:5173' },
webServer: [
{
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
{
command: 'npm run dev:client',
url: 'http://localhost:5173',
reuseExistingServer: !process.env.CI,
},
],
})
E2E テストファイルの構造:
各 screen Contract につき 1 ファイル生成。必ず smoke セクションを含む:
import { test, expect } from '@playwright/test'
test.describe('{Screen名}', () => {
test('smoke: ページが正常にロードされる', async ({ page }) => {
await page.goto('{route.path}')
await expect(page).not.toHaveURL(/error/)
await expect(page.getByRole('heading')).toBeVisible()
})
test('BR-XXX: ...', async ({ page }) => {
const unique = `テスト-${Date.now()}`
})
})
package.json の scripts に test:e2e と必要なら dev:client を追加(未定義の場合):
Read("package.json")
→ scripts.test:e2e が未定義の場合: Write("package.json") で追加
Step 6: RED スタブ確認
Level 2 / UI テストが意図通り FAIL していることを確認する(警告のみ、ブロックはしない):
{pkg_manager} test -- tests/contracts/level2/ 2>&1 | grep -E "passed|failed"
{pkg_manager} test -- tests/ui/ 2>&1 | grep -E "passed|failed"
passed が 1 件以上あれば警告を出力してユーザーに確認を促す。
Step 7: サマリー出力
## 生成結果
### Level 1 テスト(構造検証)
| Contract | テスト数 | ステータス |
|----------|---------|-----------|
| CON-order-create | 6 | GREEN (即実行可能) |
### Level 2 テスト(実装検証 - RED スタブ)
| Contract | テスト数 | 内訳 |
|----------|---------|------|
| CON-order-create | 31 | input:12, rules:4, state:5, errors:3, response:7 |
### UI テスト(screen Contract - RED スタブ)
| Contract | テスト数 | 内訳 |
|----------|---------|------|
| CON-order-form | 12 | validation:5, submit:3, auth:2, ... |
### 生成ファイル
- tests/contracts/helpers/contract-loader.ts
- tests/contracts/level1/CON-order-create.test.ts
- tests/contracts/level2/CON-order-create.test.ts
- tests/ui/order-form/OrderFormPage.test.tsx
- ...
### 次のステップ
1. Level 1 テストが GREEN であることを確認
2. Level 2 テスト・UI テストの RED スタブを 1 つずつ実装して GREEN にする
3. 全テスト GREEN 後: `/generate-docs` で設計書を後追い生成
原則
| 原則 | 説明 |
|---|
| Contract が唯一の真実源 | テストは Contract フィールドから導出。Contract にない仕様はテストしない |
| RED は可視化 | it.skip でなく実際に FAIL する形式で生成 |
| 対象制約だけを壊す | テストデータは対象以外の全制約を満たす値を使用 |
| トレーサビリティ埋め込み | @contract + @implements + Derived from コメント |
| 冪等生成 | @generated マーカー付きファイルは再実行で上書き可能 |
エラーハンドリング
| エラー | 対応 |
|---|
.blueprint/ なし | /spec の実行を案内 |
| Contract 0 件 | /spec で Contract を作成するよう案内 |
| YAML パースエラー | エラー報告 + 該当 Contract スキップ + 他の Contract は続行 |
| フレームワーク未検出 | ユーザーに確認、デフォルト Vitest を提案 |
既存テスト(@generated あり) | 自動上書き |
既存テスト(@generated なし) | ユーザー確認後に上書き |
| 部分失敗 | 成功分は保持、失敗分を報告 |