Agent skill

tdd

機能実装やバグ修正でテストを書く・変更する作業に使用。RED-GREEN-REFACTORサイクルを適用する。

Stars 0
Forks 0

Install this agent skill to your Project

npx add-skill https://github.com/TakumiOkayasu/dotfile-work/tree/main/claude/skills/TDD

SKILL.md

Test-Driven Development

トリガー条件

以下のすべてに該当する場合に発動する:

条件
キーワード一致 TDD、テスト駆動、テストファースト、RED-GREEN、失敗するテストを先に
明示的指示 「テストを書いて」「テストから始めて」「再現テストを書いてから直して」
作業種別 新機能実装・バグ修正・リファクタリングでテストコードの作成・変更を伴う

発動しない場合(除外条件):

除外 理由
テストのインポート修正のみ 振る舞いの変更なし
CIのyaml編集のみ テストコード変更なし
E2Eテストの新規作成 本スキル対象外
typo修正のみ 実装変更なし

前提条件

本スキルを開始する前に以下が満たされていること:

  • プロジェクトにテストランナーが存在する(なければユーザーに確認)
  • テスト実行コマンドが判明している
  • 作業ブランチが作成済み(mainブランチ直接作業は禁止)

鉄則(すべてに優先)

  1. REDを確認するまで実装コードを書かない。 テスト実行→FAIL確認→実装の順序を守る。
  2. テストは仕様。 テストの期待する振る舞いをユーザー確認なしに変えない。

この2つに違反した場合: 実装コードを削除し、テストのRED確認からやり直す。

なぜ重要か: テストを先に書く理由は「正しい問いを立ててから答える」ため。RED確認のスキップは「答えに合う問いを後付けする」行為であり、テストの信頼性を根本から損なう。


ステップ0: プロジェクト検出(最初に必ず実行)

テストコードを書く前に、以下を順番に検出する:

  1. テストフレームワーク(Jest / Vitest / pytest / JUnit / RSpec / Go testing / Rust #[test] 等)
  2. テストファイルの配置規約(__tests__/*.test.ts*_test.gotests/ 等)
  3. テストの命名規約(既存テストから推測)
  4. テスト実行コマンド(npm testpytestgo test ./...cargo test 等)
  5. 関連スキルの有無(git-workflow、refactoring、code-review 等)

1つでも検出できなければユーザーに確認する。推測で補わない。


ステップ1: 適用判断

テストの書き方はコードの状態に依存する。以下のシグナルで判断し、迷ったらユーザーに確認する。

仕様の確度を判断するシグナル

確度 シグナル例
確定 型定義/APIスキーマがある、ユーザーが具体的な入出力を示した、既存テストがある
方針のみ 「〜したい」「〜な感じで」、インターフェースは未定だがゴールは明確
探索的 「試してみたい」「プロトタイプ」「どうなるか見たい」
不明 途中参加、レガシーコード、ドキュメントなし

テスタビリティを判断するシグナル

状態 シグナル例
テスタブル 純粋関数、DI済み、モック可能な境界がある
テスタブルでない グローバル状態依存、DBを直接呼ぶ、外部APIが密結合、テストランナーすらない

判断結果→行動

仕様 \ テスタビリティ テスタブル テスタブルでない
確定 TDDサイクルを回す 特性テストで安全網→テスタブルにリファクタリング→TDD
方針のみ 統合テストを先に書き、詳細は実装しながら追加 特性テスト→テスタブルにする→統合テストから開始
探索的 ユーザーに確認: 実装先行でよいか、仕様を先に固めるか ユーザーに確認: 実装先行でよいか
不明 特性テストで現状記録→変更箇所からTDD導入 特性テストで現状記録→テスタブルにする→TDD

「探索的」は常にユーザー申告制。AIが勝手に判断して実装先行しない。


ステップ2: テストリスト作成

テストコードを書く前に、テストケースを自然言語でリストアップする。

// テストリスト例: ポイント計算機能
// □ 1000円の購入で10ポイント付与される
// □ 999円の購入ではポイント0
// □ 負の金額ではエラー
// □ ゴールド会員は2倍のポイント
// □ 0円の購入ではポイント0(境界値)

順序の決め方

  1. そのテストを通すために核となるインターフェースや型の決定が必要 → 優先度高
  2. 既存の実装に変更なく通せる → 優先度低
  3. 判断がつかない → 最も単純なケースから

テストリストは完璧でなくてよい。サイクル中に気づいたケースはリストに追加し、現在のサイクル完了後に着手する。

✅ リスト作成後、ユーザーに網羅性と優先順位を確認する。

「以下のテストケースで進めます。追加・変更・優先順位の調整はありますか?」


ステップ3: RED-GREEN-REFACTORサイクル

RED: 失敗するテストを書く

手順(この順序を守ること):

  1. テストケースを自然言語で言語化する(「何をテストするか」を明文化)
  2. テストコードを書く(1サイクルにつき1テストケースのみ
  3. テストを実行する
  4. 意図した理由でFAILすることを出力で確認する(スキップ禁止)

確認ポイント:

  • コンパイルエラーや設定ミスではなく、機能が未実装だからFAILしているか?
  • テスト名から「対象」「条件」「期待結果」が読み取れるか?

❌ FAILを確認できない場合は実装に進まない。テストを見直すかユーザーに報告する。

GREEN: 最小限の実装でテストを通す

3つのテクニックを状況に応じて使い分ける:

テクニック いつ使うか やること
仮実装(Fake It) 正しい一般化がすぐ書けない ハードコードでテストを通す
三角測量(Triangulation) 仮実装では2つ目のテストを通せない テスト追加で仮実装を一般化に追い込む
明白な実装(Obvious) 実装が一目で書ける 直接正しい実装を書く。詰まったら仮実装へ

GREENではテストを通す最小限の変更だけ行う。「ついでに」のリファクタリングはしない。

REFACTOR: テストを維持しながら改善

GREENの直後に毎回判断する。以下のいずれかに該当すれば実施:

  • 直前のGREENで明らかな重複が生まれた
  • 命名が意図を反映していない
  • 1つの関数/メソッドが複数の責務を持っている

該当しなければスキップしてよい。REFACTORは義務ではなく機会。

やること: 重複除去、命名改善、責務分離。全テストがGREENのまま。
やらないこと: 新機能追加(REDに戻る)、テストの振る舞い変更。


ステップ4: 特性テスト(レガシーコード対応)

テストがない/信頼できないコードに対する段階的導入。

フェーズ1: 現状を記録する

現在の振る舞いを「そのまま記録する」テストを書く。正しいかどうかは問わない。

分離方法はプロジェクトの慣習に合わせる。慣習がなければ以下から選択:

  • describe("CHARACTERIZATION: ...") / @Tag("characterization") / テスト名に [characterization] プレフィックス / ファイル名で分離

期待値に確信がない場合は // TODO: 仕様確認後に修正 コメントを付ける。

フェーズ2: 変更箇所にTDDを適用

  1. 変更対象の周辺に特性テストを書く(安全網)
  2. 新しい振る舞いのテストをRED-GREEN-REFACTORで追加
  3. 仕様が確認できた特性テストを通常テストに昇格

フェーズ3: 触った箇所から拡大

変更のたびにテストを充実させる。特性テストが減り、TDDテストが増える状態を目指す。


テストコード変更のルール

変更してよい場合

  • テストケースの追加
  • テストの構文エラー修正(意図を変えない範囲)
  • ユーザーからの仕様変更指示に伴う期待値の変更
  • テストヘルパー抽出など、振る舞いを変えないリファクタリング
  • インターフェース変更に伴う呼び出し方の更新(期待する振る舞い自体は不変)

禁止

  • 振る舞いが変わっていないのにアサーションの期待値を変える
  • FAILするテストを削除して緑にする
  • アサーションを緩くしてPASSさせる(== 10!= null 等)
  • ユーザー確認なしでテストの期待する振る舞いを変更する

判断基準: 「テスト対象の公開インターフェースから見て、振る舞いは変わったか?」 変わっていなければ期待値は変えない。


出力形式テンプレート

テストリスト提示時

## テストリスト: [機能名]

□ [正常系1]: [入力] → [期待結果]
□ [正常系2]: [入力] → [期待結果]
□ [境界値]: [入力] → [期待結果]
□ [異常系1]: [入力] → [期待結果(例外/エラー)]

追加・変更・優先順位の調整はありますか?

RED確認報告時

## RED確認 ✅

テスト: [テスト名]
実行結果: FAIL
失敗理由: [実装が未存在 / 期待値 X に対し実際は Y]

→ 実装に進みます。

GREEN完了報告時

## GREEN完了 ✅

テスト: [テスト名]
実行結果: PASS
実装内容: [1〜2行で説明]

→ REFACTORを判断します。[実施する/スキップ(理由)]

サイクル完了報告時

## サイクル完了

完了: [N]件 PASS / 残り: [M]件
次のテストケース: [ケース名]

続行しますか?

AI固有の注意点

よくある問題 対策
テストと実装を1回のレスポンスで同時に出す テスト出力→実行→RED確認→実装出力の順序を守る
実装を見てからテストを逆算する テストリストを先に作成する。実装コードを見る前にテストを書く
弱いアサーション(toBeDefined() のみ) 具体的な値を検証する。最低限「期待する戻り値」をアサート
境界値・異常系の欠落 テストリストに null、空文字列、0、負数、境界値を明示的に含める
モックの戻り値を推測で決める モック対象の実際のインターフェース(型定義/APIドキュメント)を確認してから作成
RED確認をスキップ テスト実行コマンドを毎回実行し、FAILを出力で確認する

RED確認のスキップが最も危険。 テストが最初からPASSする場合、そのテストは何も検証していない可能性がある。「意図した理由でFAILする」ことの確認が品質の要。


ユーザー確認ポイント

すべてのステップで確認を求めると開発リズムが崩れる。以下のタイミングで確認する:

タイミング 確認テンプレート
テストリスト作成後 「以下のテストケースで進めます。追加・変更・優先順位の調整はありますか?」
最初のRED-GREEN完了後 「この方向性(テストの書き方、実装アプローチ)で残りも進めてよいですか?」
仕様に曖昧さがある時 「[具体的な入力]の場合、期待する振る舞いは[A]と[B]のどちらですか?」
テストの期待値を変更したい時 「テスト[名前]の期待値を[旧]→[新]に変更してよいですか? 理由: [理由]」
一連のサイクル完了後 「[N]件のテストがすべてPASSしています。次のステップに進みますか?」

方向性が確定した後の反復的なRED-GREEN-REFACTORは、まとめて進めてよい。


コミット戦略

全テストがPASSした状態でのみコミットする。コミットメッセージはプロジェクト慣習に従う(git-workflowスキルがあれば参照)。

タイミング コミット例
GREEN完了(テスト+実装) feat: implement [feature] with tests
REFACTOR完了 refactor: [description]
特性テスト追加 test: add characterization test for [target]
バグ修正(再現テスト+修正) fix: [description]

RED(FAILするテスト)単独ではコミットしない。RED+GREENをセットでコミットする。


関連スキルとの連携

スキル 連携ポイント
refactoring REFACTORフェーズで参照。テストが緑の状態を維持しながら構造改善
git-workflow コミットメッセージ規約、ブランチ命名に従う
code-review テストの品質レビュー観点を参照
systematic-debugging RED確認で予期しないエラーが出た場合の根本原因分析

禁止事項

禁止操作 理由
RED確認をスキップして実装に進む テストの信頼性を損なう
テストを書かずに実装する(ユーザー承認なし) TDDの目的に反する
振る舞いが変わっていないのにテストの期待値を書き換える 仕様の改ざん
複数テストケースを1サイクルで同時に扱う サイクルの粒度違反
FAILするテストを削除/無効化して緑にする 問題の隠蔽
既存テストをユーザー確認なしに変更・削除する 仕様の無断変更
FAILする状態でコミットする CI破壊
モックの戻り値をインターフェース確認なしに推測で決める ハルシネーション
「探索的」かどうかをAIが勝手に判断する ユーザー申告制
テストと実装を同一レスポンスで同時出力する RED確認の迂回

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results