mcpField
T
tdd

tdd

github.com/pandoscopeテスト・品質11.9k 回インストール5 週間前に更新
リポジトリ

tdd について

コーディングエージェント向けのテスト駆動開発スキルです。失敗するテストを先にコミットし、最小限の実装とリファクタリングを行う手順を定めます。コミットの検査方法も示しつつ、テストが正しい理由で失敗したかは検査できないと説明します。

主な機能

  1. 01失敗するテストを先にコミット
  2. 021サイクルで1つの振る舞いを実装
  3. 03RED、GREEN、リファクタのコミットを分離
  4. 04マーカーとスクリプトによる構造検査
  5. 05公開インターフェース経由で動作を検証

活用シーン

  1. 01TDDサイクルに沿った機能開発
  2. 02不具合修正の回帰テスト作成
  3. 03RED/GREEN規約に沿ったコミットの確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/pandoscope/skills --skill tdd

インストール後、Claude Code で/tddを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

deep-modules.md
interface-design.md
lint-red.sh
mocking.md
refactoring.md
testing-anti-patterns.md
tests.md

詳細

テスト駆動開発

まずテストを書きます。失敗する状態でコミットします。テストを通すための最小限のコードを書きます。成功する状態でコミットします。リファクタリングします。

基本原則: テストは実装ではなく、公開インターフェースを通じて振る舞いを検証します。コードは全面的に変更できますが、テストは変えるべきではありません。内部関数の名前を変更しただけで(振る舞いに変化がないのに)失敗するテストは、実装をテストしていました。削除してください。

失敗を確認していなければ、そのテストが正しいものをテストしている証拠はありません。コミット履歴が証拠となり、フックがその構造をチェックします。

例は tests.md、モックのガイドラインは mocking.md を参照してください。

絶対のルール

失敗するテストを先にコミットせずに、本番コードを書いてはいけません。

テストより先にコードを書いてしまいましたか? そのコードを削除してください。テストをもとに新たに実装します。削除とは削除することです。例外(使い捨てのプロトタイプ、生成コード、設定)には人間の承認が必要です。「今回だけ TDD を省略しよう」と考えていませんか? それは正当化にすぎません。

1. 計画(コードを書く前に)

プロジェクトのドメイン用語集を使い、テスト名とインターフェースの語彙をコードベースに合わせます。変更する領域の ADR を尊重してください。

  • 実装手順ではなく、テストする 振る舞い を列挙します。重要な経路と複雑なロジックを優先してください。すべてをテストすることはできません。
  • テスト容易性を考慮してインターフェースを設計し、ディープモジュール(小さなインターフェースと深い実装)の機会を見極めます。
  • 公開インターフェースと優先する振る舞いをユーザーと確認してから、作業を進めます。

2. 水平ではなく、垂直スライスで

テストをすべて書いてから、実装をすべて書いてはいけません。それは水平スライスであり、ひどいテストを生みます。まとめて書かれたテストは、想像上の 振る舞いや物事の 形(シグネチャ、データ構造)をテストし、実際の変更に反応しなくなり、コードを理解する前にテストの構造に縛られてしまいます。

垂直スライスで作業します。テストを1つ書き、実装を1つ行う。このサイクルを繰り返します。それぞれのテストは、直前のサイクルで得た学びを反映します。

WRONG (horizontal):  RED: t1 t2 t3 t4   GREEN: i1 i2 i3 i4
RIGHT (vertical):    t1→i1  t2→i2  t3→i3 ...

最初のスライスはトレーサーバレットです。経路がエンドツーエンドで機能することを証明します。

3. コミット手順

1つの振る舞いにつき、RED コミットを1つ、GREEN コミットを1つ作成します。1つのブランチで複数のサイクルを行ってもかまいません。Go/Rust のリポジトリでは、マーカーを参照し、進行中のサイクルは1つにすることを推奨します。

RED を含め、すべてのコミットでツリーが lint-clean であること。 各コミットの後に、リポジトリ全体を対象として、フック一式を実行します(例:prek run --all-files)。終了コードが 0 で、ファイルが変更されていないことを確認してください。自動修正によってファイルが変更された場合は失敗です。次のサイクルを始める前に修正を適用し、問題を持ち込んだコミットに amend してください。PR の先頭だけを評価する CI であっても、中間コミットが不適切な状態であることの言い訳にはなりません。コミットごとの衛生状態は、ここで、コミット時に確認するか、まったく確認しないかのどちらかです。

STUB コミット(RED テストに不足している API サーフェスが必要な場合のみ)

RED コミットで失敗してよいのは テストのみ です。lint、フォーマット、型チェックは通る必要があります。新しいテストがまだ存在しない関数、メソッド、またはパラメーターを呼び出す場合、型チェックに失敗します。そのため、まずシグネチャだけの最小限の stub を独立したコミットとして作成し、chore(stub): を先頭に付けます。シグネチャと docstring のみで、振る舞いはありません。新しい呼び出し可能要素は NotImplementedError を送出し、新しいパラメーターは受け取って無視します。これは絶対のルールで認められる唯一の例外です。stub が定義するのは インターフェース であり、実装 を含めてはいけません。stub コミットにロジックが含まれていれば、失敗するテストなしの本番コードです。サーフェスがすでに存在する場合は、この手順をまるごと省略してください。

RED コミット
  • 失敗するテストを1つ書きます。1つの振る舞いに絞り、明確な名前を付け、実際のコードを使います。避けられない場合を除き、モックは使いません。
  • マーカーなしで実行し、正しい理由で失敗することを確認します(機能がないため、振る舞いが誤っているため、アサーションが失敗するため、またはスタブの NotImplementedError のため)。タイプミス、インポートエラー、シンボル不足は理由として認められません。すぐに成功しますか? 既存の振る舞いをテストしています。テストを修正してください。
  • マーカー(以下を参照)を追加し、テストスイートを成功させてコミットします。テストのみを変更し、接頭辞は test(red): とします。
言語マーカー厳密性
Python (pytest)@pytest.mark.xfail(strict=True)あり — XPASS はテストスイートを失敗させます
TS/JS (vitest)test.fails(...) / it.fails(...)あり
TS/JS (jest)it.failing(...)あり
Go//go:build red + TestRed の接頭辞 + red-tests ジョブ集約のみ
Rust#[ignore = "red"] + red_ の接頭辞 + red-tests ジョブ集約のみ
その他厳密な想定失敗の仕組みを探します。存在しない場合は、マーカーなしでコミットし、リポジトリに RED の強制機能がないことを人間に伝えます該当なし

Go/Rust のビルドタグ/無視マーカーは、通常のテストスイートからテストを除外するため、red-tests CI ジョブではマークされたテストのみを実行し、失敗を想定する必要があります(該当するテストがない場合は何もしません)。終了コードは集約結果です。RED テストが2つ進行中で、そのうち1つが誤って成功しても、ジョブ自体は成功します。厳密なマーカーならテストごとに検出できますが、ジョブでは検出できません。

GREEN コミット
  • テストを通すための最もシンプルなコードを書きます。YAGNI — 将来を見越した汎用化はしません。
  • 今回のサイクルで追加した RED マーカーを削除します。このコミットでは、それ以外のテスト変更はしません。
  • テストスイート全体を成功させ、出力に問題がないことを確認します。失敗しますか? テストではなくコードを修正してください。接頭辞は feat:/fix: とします。
REFACTOR(GREEN の後のみ)

重複を取り除き、名前を改善し、ヘルパーを抽出し、モジュールをより充実させます。テストは成功したまま、新しい振る舞いを追加せず、別のコミットにします。RED の間は絶対にリファクタリングしないでください。 その後、次のサイクルを始めます。

すべてのテストが成功したら、リファクタリング候補を確認します。

強制の仕組み — とその限界

lint-red.sh(スキルに含まれています。prek と CI に組み込んでください):staged/commit モードでは、test(red): コミットがテストファイルのみを変更し、マーカーを追加していること、その他のコミットではマーカーを追加していないことを確認します。merge モードでは、ツリー内にマーカーが1つでもあれば拒否します。Go/Rust のマーカーがある場合は、red-tests ジョブが存在することも確認します。

フックが確認するのは、コミットの構成とマーカーの適切な扱いです。マーカーなしのテストを実行し、正しい理由で失敗するのを確認したかどうかは検証しません。そこは自分で責任を持つ必要があります。コミットごとの Lint も同様です。インストール済みの pre-commit フックはコミット時に汚れたコミットを阻止しますが、フックがインストールされていない環境(新しいリモート環境など)では、コミット後に毎回実行する --all-files は自分で徹底しなければなりません。それでも必ず実行してください。厳密なマーカーは一部を補えます(XPASS によって、すでに存在する振る舞いをテストしていることを検出できます)。「Lint が通った」≠「TDD が検証された」です。

良いテスト

品質ルール
振る舞いを確認するパブリック API を通る実際の経路をテストし、リファクタリング後も有効であること
最小限1つのことに絞ります。名前に「そして」が含まれていますか? 分割してください。
明確test1 ではなく、振る舞いを名前で表します
誠実モックではなく、コードをテストします
// Good — tests real behavior (vitest; jest: it.failing)
test.fails('retries failed operations 3 times', async () => {
  let attempts = 0;
  const op = () => { attempts++; if (attempts < 3) throw new Error('fail'); return 'ok'; };
  expect(await retryOperation(op)).toBe('ok');
  expect(attempts).toBe(3);
});

危険信号 — STOP、削除してやり直す

実装前なのにマーカーなしのテストが成功する · なぜテストが失敗したのか説明できない · GREEN で通すためにアサーションを弱める · モックをテストする · 「今回だけ」 · 「現実的に対応しているだけ、TDD は教条的だ」

よくある落とし穴を避けるには、testing-anti-patterns.md を読んでください。

合理化一覧

言い訳実際のところ
「テストするほどでもないほど簡単」簡単なコードでも壊れます。テストにかかる時間は30秒です。
「後でテストする」後から書くテストは実装に引きずられ、「これは何をするか?」を問うのであって、「これは何をすべきか?」を問いません。
「何時間も書いたコードを削除するのはもったいない」サンクコストです。検証されていないコードは負債です。
「テストを書くのが難しい」テストしにくいものは、使いにくいものです。そのことを受け止め、インターフェースをシンプルにしてください。
「すべてモックしなければならない」結合しすぎています。依存関係を注入してください。
「Lint が通ったから TDD は完了」フックが確認するのは構成であり、失敗を自分で確認したかどうかではありません。

行き詰まったとき/デバッグ

テスト方法がわからない場合は、まず望ましい API とアサーションを書きます。バグを見つけた場合は、それを再現する失敗するテストを書いてから、プロトコル全体を実行します。テストなしでバグを修正してはいけません。

コメント