- 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: コンテキスト読み込み
```bash
# git root を検出
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 テスト生成
1. `tests/contracts/helpers/contract-loader.ts` を生成(存在しない場合)
2. 各 Contract ごとに:
- `Read()` で Contract YAML を読み込み
- `{baseDir}/references/test-generation-rules.md` の Level 1 ルールに従って生成
- `Write()` で `tests/contracts/level1/CON-{name}.test.ts` に出力
**`@generated` マーカー**: 各ファイル先頭に以下を付与:
```typescript
// @generated by /test-from-contract
// @contract CON-{name}
// @implements {links.implements の値}
```
### Step 5: Level 2 テスト生成
1. 各 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` に出力
2. 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`
3. タイプ別ヘルパーを生成(`tests/contracts/helpers/{type}-helpers.ts`)
**各テストの導出元コメント**: テスト内に Contract フィールドへの参照を埋め込む:
```typescript
it("BR-001: サーバー側で金額再計算", () => {
// Derived from: business_rules[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 の場合):
```typescript
import { defineConfig } from '@playwright/test'
export default defineConfig({
testDir: './tests/e2e',
use: { baseURL: 'http://localhost:5173' },
webServer: [
{
command: 'npm run dev', // package.json から適切なコマンドを検出
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
{
command: 'npm run dev:client', // Vite 起動コマンドを検出
url: 'http://localhost:5173',
reuseExistingServer: !process.env.CI,
},
],
})
```
**E2E テストファイルの構造**:
各 screen Contract につき 1 ファイル生成。必ず smoke セクションを含む:
```typescript
// @generated by /test-from-contract
// @contract CON-{screen-name}
import { test, expect } from '@playwright/test'
test.describe('{Screen名}', () => {
// --- smoke: 全 screen Contract に必須 ---
test('smoke: ページが正常にロードされる', async ({ page }) => {
await page.goto('{route.path}')
await expect(page).not.toHaveURL(/error/)
// 主要要素の存在確認
await expect(page.getByRole('heading')).toBeVisible()
})
// --- 品質保証: business_rules から導出 ---
// BR-XXX: フォーム送信 → ページ遷移 → 結果反映
test('BR-XXX: ...', async ({ page }) => {
const unique = `テスト-${Date.now()}`
// Derived from: business_rules[BR-XXX]
// ...
})
})
```
package.json の scripts に `test:e2e` と必要なら `dev:client` を追加(未定義の場合):
```
Read("package.json")
→ scripts.test:e2e が未定義の場合: Write("package.json") で追加
```
### Step 6: RED スタブ確認
Level 2 / UI テストが意図通り FAIL していることを確認する(警告のみ、ブロックはしない):
```bash
# .blueprint/config.yaml から package_manager + frontend.test_tool を取得して実行
# Level 2 RED スタブ確認
{pkg_manager} test -- tests/contracts/level2/ 2>&1 | grep -E "passed|failed"
# → passed 0 件であれば OK
# UI テスト RED スタブ確認(screen Contract がある場合)
# config.yaml の tech_stack.frontend.test_tool に応じて分岐:
# testing-library / vitest → {pkg_manager} test -- tests/ui/
# playwright → {pkg_manager} exec playwright test tests/ui/
{pkg_manager} test -- tests/ui/ 2>&1 | grep -E "passed|failed"
# → passed 0 件であれば OK(Playwright は出力フォーマットが異なる場合あり)
```
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` なし) | ユーザー確認後に上書き |
| 部分失敗 | 成功分は保持、失敗分を報告 |
在 GitHub 查看