fable-tdd について
fable-tddは、信頼できるテストで動作の欠落や不具合を示してから、テストを満たす最小限の変更を行う開発スキルです。REDが対象の動作に起因することを確かめ、実装後は周辺の失敗ケースも確認します。
主な機能
- 01入力・操作・観測可能な結果を定義
- 02テスト環境が対象経路を実行するか確認
- 03動作を証明できるテストレベルを選択
- 04有効なREDの後に本番コードを変更
- 05周辺の境界条件と失敗ケースを確認
- 06最新の検証証拠を添えて引き継ぎ
活用シーン
- 01再現可能なバグや回帰の修正
- 02安定した契約に基づく動作の追加
- 03入力検証・状態遷移・永続化のテスト
- 04リファクタリング前の不変条件の記録
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ immamdouhaboammar/ get-fable --skill fable-tddインストール後、Claude Code で/fable-tddを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
skill.package.jsonagents/openai.yamlevals/scenarios.jsonexamples/failing-test-first.mdreferences/red-green-refactor.mdtest-strategy-and-hard-cases.mdtemplates/tdd-cycle.template.md詳細
Fable TDD
本番コードを変更する前に、動作が欠けている、または壊れていることを証明し、適切なテストレベルで適切なテストを満たす最小限の変更を行います。
ミッション
TDD は「まず何でもいいから失敗するテストを書く」ことではありません。信頼できるハーネスを通じて、意図する動作に不足があることを RED 状態で示す必要があります。構文エラー、壊れたフィクスチャ、古いビルド、モックだけの期待値では、本番コードを変更する正当な理由にはなりません。
このスキルが重視するのは、次の 3 点です。
- 因果関係への確信:動作が間違っているためにテストが失敗すること。
- 最小限の介入:実装の変更を、動作に必要な範囲だけにとどめること。
- 永続的な回帰検証:バグが再発した場合に、そのテストで検出できること。
有効にする場合
- 再現可能なバグやリグレッションを修正する場合。
- 安定した契約があり、検証可能な動作を追加する場合。
- バリデーション、状態遷移、計算、API の動作、永続化、統合のセマンティクスを変更する場合。
- 不変条件を実行可能な形で先に特定する必要があるリファクタリングを行う場合。
有効にしない場合
- 動作の場所を特定できない、または再現できない場合(
fable-discover)。 - 主な不確実性が外部 API のセマンティクスにある場合(
fable-research)。 - 動作テストに意味がない、実行を伴わないドキュメントやメタデータを変更する場合(
fable-execute)。 - 必要なテストハーネス自体が壊れている、または古い成果物を実行している場合(
fable-recover)。
変更の分類
テストを選ぶ前に、動作を分類します。
| 分類 | 推奨する検証方法 |
|---|---|
| 純粋なロジック/ドメインロジック | 焦点を絞ったユニットテスト/プロパティテスト |
| 境界でのバリデーション/エラーマッピング | 境界でのユニットテストまたは契約テスト |
| モジュール間の連携 | 変更対象の契約を通した統合テスト |
| データベース/キュー/キャッシュの動作 | 可能であれば現実的な境界を使った統合テスト |
| HTTP/CLI/公開 API | 公開エントリーポイントでの契約テスト/統合テスト |
| UI のユーザーフロー | まずコンポーネントテスト/統合テスト。高価値な境界横断の動作には E2E テスト |
| 並行処理/タイミング | スリープして期待するのではなく、決定的な調整を行うテスト |
| 適切なシームがないレガシー動作 | 最も近い安定した境界での特性テスト |
| リファクタリング/意図した動作変更なし | 移動の前に特性テスト/不変条件テスト |
テスト対象そのものをモックで取り除くことなく、実際の動作を証明できる最も低いテストレベルを使います。
プロトコル
ステージ 1 — 動作の契約を書く
次の内容を明記します。
- 初期状態/入力。
- トリガー/アクション。
- 期待する観測可能な結果。
- 関連する副作用。
- エラー/境界での動作。
- 維持すべき不変条件。
バグの場合は、具体的な再現手順と、提案する実装を分けて記録します。
ステージ 2 — ハーネスを検証する
RED に進む前に、選んだテストが関連する経路を実際に実行することを確認します。
該当する場合は、次の点を確認します。
- ソースコードとビルド済み成果物のどちらを使っているか。
- テストの検出/設定。
- フィクスチャの現実性。
- 機能フラグ/環境変数。
- モックの境界。
- 非同期処理の完了。
- クリーンアップ/分離。
- アサーションが内部の呼び出し回数ではなく、公開された動作を観測しているか。
ステージ 3 — テストレベルを選ぶ
次の点を確認します。
- どのような本番コードの変更をすると、このテストが再び失敗しますか?
- バグが実際にある契約の境界を、テストは通過していますか?
- 疑わしい失敗をモックで取り除いていませんか?
- より狭いテストで、同じユーザーに見える動作をより決定的に証明できますか?
#1 に答えられない場合、そのテストはおそらく弱いものです。
ステージ 4 — RED
動作の契約を捉えた最小限のテストを書き、実行します。
RED が有効なのは、次の条件を満たす場合に限ります。
- テストが実行される。
- 失敗が、理由を判断できる程度に決定的である。
- 失敗メッセージ/状態が、想定する動作の欠落/破損と一致する。
- セットアップ、構文、インポート、環境、古い成果物、または無関係なテストの汚染が原因で失敗していない。
失敗の理由が異なる場合は、テスト/ハーネスを修正して RED をやり直します。まだ本番コードには触れないでください。
ステージ 5 — 最小限の GREEN
有効な RED を満たせる、最小の本番コードの範囲を変更します。
次のことは行わないでください。
- 隣接する API を再設計する;
- 推測に基づく柔軟性を追加する;
- 無関係なコードをリファクタリングする;
- アサーションを弱める;
- GREEN にするために、実際の動作をモックに置き換える。
GREEN になるまで、対象を絞ったテストを実行します。
ステージ 6 — 隣接箇所の反証
リファクタリングの前に、変更に応じて最も近い失敗箇所を調べます。
- 境界値/空の入力/無効な入力;
- エラーパス;
- 状態遷移の順序;
- 冪等性/再試行;
- 並行処理/競合状態;
- 以前の形式/API との互換性;
- 永続化/トランザクションの動作。
意味のある契約を守る場合にのみテストを追加し、機械的にテスト数を増やさないでください。
ステージ 7 — GREEN の状態でリファクタリング
必要であれば、ここで構造を改善します。小さな手順でリファクタリングし、意味のある変更のたびに影響を受けるテストを再実行します。
ステージ 8 — 新鮮な引き継ぎ
最後の変更を記録し、次の内容を添えて fable-verify に引き継ぎます。
- 動作の契約;
- RED が示す正確な証拠/理由;
- GREEN のコマンド/結果;
- 追加/変更したテスト;
- 変更した本番コードの範囲;
- 調査した隣接ケース;
- 対象を絞ったテストではカバーされていない残存リスク。
判断ルール
- 本番コードを変更する前にテストが成功する → 要求されたギャップを証明していません。テストを再設計するか、動作がすでに存在することを確認します。
- アサーションの前にテストでエラーが発生する → 本番コードではなく、テストハーネス/テストを修正します。
- バグが非決定的である → まず非決定性を制御するか、計測します。ランダムに何度も再実行しても、信頼できる RED の証拠にはなりません。
- 並行処理のバグ → 任意の待機時間より、バリア/ラッチ/フェイククロック/決定論的なスケジューリングを優先します。
- レガシーコードにユニットテスト用の境界がない → 境界を設ける前に、最も安定した公開境界でテストします。ユニットテストを見た目よく純粋にするためだけに、大規模なリファクタリングを行わないでください。
- 外部依存関係をローカルで実行できない → 主要な根拠に基づいてフェイクが保持すべき動作を明確にした後でのみ、契約/フェイクを使用します。
- ユーザーから「とにかくパッチを当てて」と求められた → 実行可能な動作を変更する場合は、実行可能なテスト環境がないことが明示され、了承されていない限り、RED の手順を維持します。
- テストの期待値が、現在合意されている製品契約と矛盾する → 古くなったテストに実装を無理に合わせないでください。まず契約を解決します。
- モックが呼び出されたことしかアサートしないテスト → 呼び出し自体が公開契約である場合を除き、観測可能な結果/状態の証拠を追加します。
不変条件
- テスト可能な変更では、有効な RED が出る前に本番の動作を変更しません。
- RED と GREEN は同じ動作の契約を対象とします。
- 実装を通すためにテストを弱めません。
- 想定される失敗の仕組みをモックで回避しません。
- 最後の関連変更の後に、改めて GREEN を確認します。
- リファクタリングによって、合意済みのカードの範囲外の動作を追加しません。
失敗の分類
誤った RED
失敗の原因が、対象の動作ではなく構文/インポート/セットアップ/フィクスチャ/環境にある状態です。まずテストハーネスを修正します。
偽の GREEN
テストは成功しますが、モックや古い成果物が実際の失敗境界を迂回しているなど、現実の失敗境界を通過していません。テストを強化するか、配置を見直します。
不安定な RED/GREEN
関連コードを変更していないのに結果が変わります。いずれの状態も証拠として扱う前に、非決定性を特定します。
テスト不能なレガシーの範囲
狭い境界が存在しません。まず安定した境界で特性を確認し、その後、テストによって裏付けられる最小限の境界を導入します。
契約の曖昧さ
期待される動作そのものに議論の余地がある、または不明確な状態です。推測をテストに反映するのではなく、計画/調査に戻ります。
実装ループ
有効な RED があるにもかかわらず、実質的に似た 2 つの修正が失敗しました。編集を中止し、RED、試した内容、観測された失敗の違いを添えて fable-recover に引き継ぎます。
アンチパターン
- 実装を書いてから、すぐに通るテストを後付けすること。
- どんな失敗でも RED として受け入れること。
- 現在の実装に合うように期待される出力を変更すること。
- ユーザーに見える状態を検証できるのに、モックとのやり取りだけを検証すること。
- 競合状態の「テスト」にスリープを使うこと。
- 1つの動作を検証する前に、複雑なテスト抽象化を構築すること。
- 特性を明らかにする前に、レガシーコードを広範囲にリファクタリングすること。
- 統合境界でしか動作を検証できないのに、単体テストを無理に行うこと。
- リファクタリング後に、新たな GREEN の確認を省略すること。
- カバレッジ率を回帰の証明と同一視すること。
エビデンスパケット
Behavior contract:
Test level + why:
RED command/result + expected failure reason:
Production mutation:
GREEN command/result:
Adjacent cases probed:
Refactor mutations:
Final fresh GREEN:
Residual risk / verify next:
完了基準
TDD は、以下を満たした時点で完了します。
- 有効な RED によって動作上のギャップが実証されていること。
- その RED の後にのみ、本番コードが変更されていること。
- 同じ契約に対して、対象を絞った GREEN が確認されていること。
- 関連する重要な失敗箇所が検討されていること。
- リファクタリング後、または最後の変更後に、テストが改めて実行されていること。
fable-verifyが結果を独自に反証できるだけのエビデンスを受け取っていること。
段階的なリソース
- 詳細な戦略:
references/test-strategy-and-hard-cases.md - 既存のサイクルに関するリファレンス:
references/red-green-refactor.md - 例:
examples/failing-test-first.md