tdd-guide について
テスト生成、カバレッジ不足の分析、レッド・グリーン・リファクタリングを支援する開発者向けのテスト駆動開発スキルです。Jest、Pytest、JUnit、Vitestに対応し、フィクスチャ生成などのスクリプトも備えています。
主な機能
- 01コードや要件からのテスト生成
- 02カバレッジ不足の分析と優先順位付け
- 03レッド・グリーン・リファクタリングの支援
- 04テストデータとモックの生成
- 05プロパティテストとミューテーションテストの案内
活用シーン
- 01既存コードのユニットテスト作成
- 02未カバーのコード経路の特定
- 03受け入れ基準に沿った機能開発
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ alirezarezvani/ claude-skills --skill tdd-guideインストール後、Claude Code で/tdd-guideを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
HOW_TO_USE.mdREADME.mdassets/expected_output.jsonsample_coverage_report.lcovsample_input_python.jsonsample_input_typescript.jsonreferences/ci-integration.mdframework-guide.mdtdd-best-practices.mdscripts/coverage_analyzer.pyfixture_generator.pyformat_detector.pyframework_adapter.pymetrics_calculator.pyoutput_formatter.pytdd_workflow.pytest_generator.py詳細
TDD ガイド
Jest、Pytest、JUnit、Vitest におけるテストの生成、カバレッジの分析、レッド・グリーン・リファクタリングのワークフローをガイドするテスト駆動開発スキルです。
ワークフロー
コードからテストを生成する
- ソースコード(TypeScript、JavaScript、Python、Java)を提示します
- 対象のフレームワーク(Jest、Pytest、JUnit、Vitest)を指定します
- 要件を指定して
test_generator.pyを実行します - 生成されたテストのスタブを確認します
- 検証: テストがコンパイルでき、正常系、エラーケース、エッジケースを網羅していることを確認します
カバレッジの不足箇所を分析する
- テストランナーからカバレッジレポートを生成します(
npm test -- --coverage) - LCOV/JSON/XML レポートに対して
coverage_analyzer.pyを実行します - 優先順位付けされた不足箇所(P0/P1/P2)を確認します
- カバーされていない経路に対するテストを生成します
- 検証: カバレッジが目標のしきい値(通常は 80% 以上)を満たしていることを確認します
新機能の TDD
- まず失敗するテストを書きます(RED)
tdd_workflow.py --phase redを実行して検証します- テストに合格する最小限のコードを実装します(GREEN)
tdd_workflow.py --phase greenを実行して検証します- テストをグリーンのまま維持してリファクタリングします(REFACTOR)
- 検証: 各サイクル後にすべてのテストが合格することを確認します
例
テスト生成 — 入力 → 出力(Pytest)
入力ソース関数(math_utils.py):
def divide(a: float, b: float) -> float:
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
コマンド:
python scripts/test_generator.py --input math_utils.py --framework pytest
生成されたテスト出力(test_math_utils.py):
import pytest
from math_utils import divide
class TestDivide:
def test_divide_positive_numbers(self):
assert divide(10, 2) == 5.0
def test_divide_negative_numerator(self):
assert divide(-10, 2) == -5.0
def test_divide_float_result(self):
assert divide(1, 3) == pytest.approx(0.333, rel=1e-3)
def test_divide_by_zero_raises_value_error(self):
with pytest.raises(ValueError, match="Cannot divide by zero"):
divide(10, 0)
def test_divide_zero_numerator(self):
assert divide(0, 5) == 0.0
カバレッジ分析 — P0/P1/P2 の出力例
コマンド:
python scripts/coverage_analyzer.py --report lcov.info --threshold 80
出力例:
Coverage Report — Overall: 63% (threshold: 80%)
P0 — Critical gaps (uncovered error paths):
auth/login.py:42-58 handle_expired_token() 0% covered
payments/process.py:91-110 handle_payment_failure() 0% covered
P1 — High-value gaps (core logic branches):
users/service.py:77 update_profile() — else branch 0% covered
orders/cart.py:134 apply_discount() — zero-qty guard 0% covered
P2 — Low-risk gaps (utility / helper functions):
utils/formatting.py:12 format_currency() 0% covered
Recommended: Generate tests for P0 items first to reach 80% threshold.
主なツール
| ツール | 用途 | 使用方法 |
|---|---|---|
test_generator.py | コード/要件からテストケースを生成します | python scripts/test_generator.py --input source.py --framework pytest |
coverage_analyzer.py | カバレッジレポートを解析、分析します | python scripts/coverage_analyzer.py --report lcov.info --threshold 80 |
tdd_workflow.py | レッド・グリーン・リファクタリングのサイクルをガイドします | python scripts/tdd_workflow.py --phase red --test test_auth.py |
fixture_generator.py | テストデータとモックを生成します | python scripts/fixture_generator.py --entity User --count 5 |
追加スクリプト:framework_adapter.py(フレームワーク間の変換)、metrics_calculator.py(品質メトリクス)、format_detector.py(言語/フレームワークの検出)、output_formatter.py(CLI/デスクトップ/CI 出力)。
入力要件
テスト生成の場合:
- ソースコード(ファイルパスまたは貼り付けた内容)
- 対象のフレームワーク(Jest、Pytest、JUnit、Vitest)
- カバレッジの範囲(ユニット、統合、エッジケース)
カバレッジ分析の場合:
- カバレッジレポートファイル(LCOV、JSON、XML 形式)
- 任意:コンテキスト把握のためのソースコード
- 任意:目標しきい値のパーセンテージ
TDD ワークフローの場合:
- 機能要件またはユーザーストーリー
- 現在のフェーズ(RED、GREEN、REFACTOR)
- テストコードと実装の状況
仕様優先のワークフロー
TDD は、記述された仕様に基づいて進めると最も効果的です。手順は次のとおりです。
- 仕様を書く、または受け取る —
specs/<feature>.mdに保存します - 受け入れ基準を抽出する — 各基準を 1 つ以上のテストケースにします
- 失敗するテストを書く(RED) — 受け入れ基準ごとに 1 つのテストを書きます
- 最小限のコードを実装する(GREEN) — 各テストを順番に満たします
- リファクタリングする — すべてのテストがグリーンのまま、コードを整理します
仕様ディレクトリの規約
project/
├── specs/
│ ├── user-auth.md # Feature spec with acceptance criteria
│ ├── payment-processing.md
│ └── notification-system.md
├── tests/
│ ├── test_user_auth.py # Tests derived from specs/user-auth.md
│ ├── test_payments.py
│ └── test_notifications.py
└── src/
仕様からテストを抽出する
仕様の各受け入れ基準は、少なくとも 1 つのテストに対応します。
| 仕様の基準 | テストケース |
|---|---|
| 「ユーザーは有効な認証情報でログインできる」 | test_login_valid_credentials_returns_token |
| 「無効なパスワードでは 401 を返す」 | test_login_invalid_password_returns_401 |
| 「ログインに 5 回失敗するとアカウントをロックする」 | test_login_locks_after_five_failures |
ヒント: 仕様の受け入れ基準に番号を付けてください。追跡しやすいよう、テストの docstring で番号を参照します(# AC-3: Account locks after 5 failed attempts)。
関連項目: 仕様テンプレートやレビュー用チェックリストなど、仕様駆動の手法全体については
engineering/spec-driven-workflowを参照してください。
言語ごとのレッド・グリーン・リファクタリングの例
TypeScript / Jest
// test/cart.test.ts
describe("Cart", () => {
describe("addItem", () => {
it("should add a new item to an empty cart", () => {
const cart = new Cart();
cart.addItem({ id: "sku-1", name: "Widget", price: 9.99, qty: 1 });
expect(cart.items).toHaveLength(1);
expect(cart.items[0].id).toBe("sku-1");
});
it("should increment quantity when adding an existing item", () => {
const cart = new Cart();
cart.addItem({ id: "sku-1", name: "Widget", price: 9.99, qty: 1 });
cart.addItem({ id: "sku-1", name: "Widget", price: 9.99, qty: 2 });
expect(cart.items).toHaveLength(1);
expect(cart.items[0].qty).toBe(3);
});
it("should throw when quantity is zero or negative", () => {
const cart = new Cart();
expect(() =>
cart.addItem({ id: "sku-1", name: "Widget", price: 9.99, qty: 0 })
).toThrow("Quantity must be positive");
});
});
});
Python / Pytest(高度なパターン)
# tests/conftest.py — shared fixtures
import pytest
from app.db import create_engine, Session
@pytest.fixture(scope="session")
def db_engine():
engine = create_engine("sqlite:///:memory:")
yield engine
engine.dispose()
@pytest.fixture
def db_session(db_engine):
session = Session(bind=db_engine)
yield session
session.rollback()
session.close()
# tests/test_pricing.py — parametrize for multiple cases
import pytest
from app.pricing import calculate_discount
@pytest.mark.parametrize("subtotal, expected_discount", [
(50.0, 0.0), # Below threshold — no discount
(100.0, 5.0), # 5% tier
(250.0, 25.0), # 10% tier
(500.0, 75.0), # 15% tier
])
def test_calculate_discount(subtotal, expected_discount):
assert calculate_discount(subtotal) == pytest.approx(expected_discount)
Go — テーブル駆動テスト
// cart_test.go
package cart
import "testing"
func TestApplyDiscount(t *testing.T) {
tests := []struct {
name string
subtotal float64
want float64
}{
{"no discount below threshold", 50.0, 0.0},
{"5 percent tier", 100.0, 5.0},
{"10 percent tier", 250.0, 25.0},
{"15 percent tier", 500.0, 75.0},
{"zero subtotal", 0.0, 0.0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ApplyDiscount(tt.subtotal)
if got != tt.want {
t.Errorf("ApplyDiscount(%v) = %v, want %v", tt.subtotal, got, tt.want)
}
})
}
}
自律性の範囲に関するルール
自律的にテストを生成する場合は、次のルールに従って、いつ作業を中断してユーザーに確認するか判断してください。
中断して確認する場合
- 要件が曖昧な場合 — 仕様やユーザーストーリーの受け入れ基準に矛盾がある、または不明確な点がある
- エッジケースが不足している場合 — ドメイン知識がなければ境界値を判断できない(例:トランザクション金額の上限)
- テスト数が50件を超える場合 — 大規模なテストスイートはコミット前に人間によるレビューが必要です。概要を提示し、優先する領域を尋ねてください
- 外部依存関係が不明確な場合 — この機能が、動作が文書化されていないサードパーティ製 API やサービスに依存している
- セキュリティ上重要なロジックの場合 — 認証、認可、暗号化、または決済フローのテストシナリオには、人間の承認が必要です
自律的に続行する場合
- 番号付きの受け入れ基準がある明確な仕様 — 各基準をテストに直接対応させられる
- 単純な CRUD 操作 — 明確に定義されたモデルでの作成、読み取り、更新、削除
- 明確に定義された API コントラクト — OpenAPI 仕様または型付きインターフェースが利用できる
- 純粋関数 — 副作用がなく、入力と出力が決定的である
- 既存のテストパターン — コードベースに、参考にできる類似のテストがすでにある
プロパティベーステスト
プロパティベーステストでは、厳選した例に頼るのではなく、ランダムな入力を生成して不変条件を検証します。入力空間が広く、期待される動作をプロパティとして記述できる場合に使用してください。
Python — Hypothesis
from hypothesis import given, strategies as st
from app.serializers import serialize, deserialize
@given(st.text())
def test_roundtrip_serialization(data):
"""Serialization followed by deserialization returns the original."""
assert deserialize(serialize(data)) == data
@given(st.integers(), st.integers())
def test_addition_is_commutative(a, b):
assert a + b == b + a
TypeScript — fast-check
import fc from "fast-check";
import { encode, decode } from "./codec";
test("encode/decode roundtrip", () => {
fc.assert(
fc.property(fc.string(), (input) => {
expect(decode(encode(input))).toBe(input);
})
);
});
例ベーステストよりプロパティベーステストを使う場面
| プロパティベーステストを使う場面 | 例 |
|---|---|
| データ変換 | シリアライズ/デシリアライズの往復 |
| 数学的性質 | 可換性、結合性、冪等性 |
| エンコード/デコード | Base64、URL エンコード、圧縮 |
| 並べ替えとフィルタリング | 出力がソートされている、長さが保持されている |
| パーサーの正しさ | 有効な入力は常にエラーなく解析される |
ミューテーションテスト
ミューテーションテストでは、本番コードを変更して(「ミュータント」を作成し)、テストで変更を検出できるかを確認します。ミュータントが生き残る(テストが引き続き成功する)場合、カバレッジだけでは明らかにならないテストの抜けがあります。
ツール
| 言語 | ツール | コマンド |
|---|---|---|
| TypeScript/JavaScript | Stryker | npx stryker run |
| Python | mutmut | mutmut run --paths-to-mutate=src/ |
| Java | PIT | mvn org.pitest:pitest-maven:mutationCoverage |
ミューテーションテストが重要な理由
- 行カバレッジ100% ≠ 優れたテスト — カバレッジでわかるのはコードが実行されたことだけであり、検証されたことではありません
- 弱いアサーションを検出する — コードは実行するものの、意味のあるアサーションを行わないテストを見つけます
- 境界値テストの不足を見つける —
<を<=に変更するミュータントは、オフバイワンの抜けを明らかにします - 定量化できる品質指標 — ミューテーションスコア(検出されたミュータントの割合)は、カバレッジ率よりも有力な指標です
推奨: 全体のカバレッジが高くても、重要な経路(認証、決済、データ処理)ではミューテーションテストを実行してください。P0 モジュールでは85%以上のミューテーションスコアを目指してください。
相互参照
| スキル | 関連性 |
|---|---|
engineering/spec-driven-workflow | 仕様 → 受け入れ基準 → テスト抽出のパイプライン |
engineering-team/focused-fix | フェーズ5(検証)では、回帰テストを使って修正を確認するために TDD を使用 |
engineering-team/senior-qa | より広範な QA 戦略。TDD はテストピラミッドの一層です |
engineering-team/code-reviewer | 生成されたテストのアサーション品質とカバレッジの網羅性をレビュー |
engineering-team/senior-fullstack | プロジェクトスキャフォルダーには、TDD ワークフローと互換性のあるテスト基盤が含まれます |
制限事項
| 対象範囲 | 詳細 |
|---|---|
| 単体テストに重点 | 統合テストとE2Eテストには異なるパターンが必要です |
| 静的解析 | テストの実行や実行時の動作の測定はできません |
| 対応言語 | TypeScript、JavaScript、Python、Javaに最適です |
| レポート形式 | LCOV、JSON、XMLのみです。それ以外の形式は変換が必要です |
| 生成されるテスト | ひな形を提供します。複雑なロジックには人によるレビューが必要です |
ほかのツールを使用する場面:
- E2Eテスト:Playwright、Cypress、Selenium
- パフォーマンステスト:k6、JMeter、Locust
- セキュリティテスト:OWASP ZAP、Burp Suite