Claude Code にコードを書かせると、テストなしでいきなり実装を始めてしまう。心当たりはありませんか?
「ログイン機能を作って」と頼むと、認証ロジックからルーティングまで一気に書き上げてくれる。動くコードが出てくるのは嬉しいけれど、テストはどこにもない。後からテストを追加しようとすると、実装に合わせた「答え合わせ」テストになってしまい、バグを見つける力がない。
これは Claude Code が悪いわけではありません。指示の出し方の問題です。
公式ドキュメントでも「Claude に自分の作業を検証する手段を与えることが、最もインパクトの大きい施策」と明言されています。テストがまさにその検証手段であり、TDD はそれを最も効果的に機能させるワークフローです。
なぜ Claude Code と TDD は相性がいいのか
TDD は「テストを先に書き、テストが失敗する状態(Red)を確認してから、テストを通す実装を書く(Green)」という開発手法です。手動でやると正直めんどくさい。テストを書く → 実行する → 実装を書く → また実行する。この反復を丁寧に続ける気力が持たないことも多いでしょう。
ところが Claude Code を使うと、この面倒な部分を AI が担ってくれます。
1. テストが「明確なゴール」になり、Claude の精度が上がる
テストが先にあると、Claude は「このテストを通せばOK」というゴールが明確になり、的外れな実装をしにくくなります。曖昧な要件を渡すよりも、具体的なテストケースを渡すほうが、はるかに正確なコードが返ってきます。
2. Red → Green のフィードバックループが高速で回る
Claude Code はテストの実行とコード修正を自動で繰り返せます。人間が手でやると数分かかるサイクルが、数十秒で回ります。
3. リファクタリングが怖くなくなる
テストが先にある状態で Claude にリファクタリングを頼めば、「テストが通っているから大丈夫」という安全ネットが常にあります。つまり、攻めたリファクタリングが可能になるわけです。
Claude Code が TDD を破る3つのパターン
ただし、何も指示しないと Claude Code は TDD を守りません。
パターン1:実装を先に書いてしまう。 最も多い問題です。AI にとっては「動くコードを早く出す」のが自然な振る舞いだからです。
パターン2:実装に合わせたテストを書く。 実装後にテストを書かせると、自分のコードを追認するだけのテストになります。エッジケースを見落としたまま正常系だけ通る。
パターン3:テストを通すために実装を歪める。 ハードコードされた戻り値を返したり、テストケースに特化した分岐を入れたりして、汎用性のないコードが出来上がります。
TDD を守らせるプロンプト術:悪い例 vs 良い例
悪い例 1:丸投げ
パスワードリセット機能を作って。テストも書いて。
「テストも書いて」は「ついでに」程度の意味にしかなりません。Claude は実装を先に書きます。
悪い例 2:手順があいまい
TDD で開発して。テストファーストで。
「TDD で」と言っても、Claude がどのタイミングでテストを実行するかは保証されません。
良い例:手順とテストケースを明示する
パスワードリセット機能を TDD で開発してほしい。
以下の手順を厳密に守ること:
1. まず失敗するテストを1つだけ書く
2. テストを実行して Red(失敗)を確認する
3. テストを通す最小限の実装を書く
4. テストを実行して Green(成功)を確認する
5. 必要ならリファクタリングする
6. 次のテストケースに進み、1-5 を繰り返す
テストケース:
- 有効なメールアドレスでリセットトークンが生成される
- 存在しないメールアドレスではエラーを返す
- 期限切れトークンではリセットが失敗する
- 空文字列の入力ではバリデーションエラーを返す
「テストを実行して Red を確認する」の一文が最も重要です。 これがないと Claude はテストを書いた直後に実装まで書いてしまい、Red の状態を確認しません。
実践:API エンドポイントを TDD で作る
calculatePrice のような小さな関数だけでなく、実際のアプリケーション開発で TDD がどう回るかを見てみましょう。ブックマーク API を例にします。
ステップ1:テストを書かせる(Red)
POST /api/bookmarks のテストを先に書いて。実装はまだ書かないこと。
テストファイル: src/app/api/bookmarks/__tests__/route.test.ts
仕様:
- ログイン済みユーザーが courseId を送信するとブックマークが作成される
- 未ログインなら 401 を返す
- 存在しない courseId なら 404 を返す
- 同じ講座を2回ブックマークしたら 409 を返す
テストを書いたら実行して、すべて FAIL になることを確認して。
Claude がテストを書いて実行すると、こんな出力になります。
FAIL src/app/api/bookmarks/__tests__/route.test.ts
✕ ログイン済みユーザーがブックマークを作成できる
✕ 未ログインなら 401 を返す
✕ 存在しない courseId なら 404 を返す
✕ 重複ブックマークなら 409 を返す
Tests: 4 failed, 4 total
4つとも Red。これが正しい出発点です。
ステップ2:最小限の実装を書かせる(Green)
今書いたテストをすべて通す最小限の実装を
src/app/api/bookmarks/route.ts に書いて。
テストを実行して全部 Green になることを確認して。
余計な機能は追加しないこと。
「最小限の」と「余計な機能は追加しないこと」がポイントです。これがないと、Claude はページネーションやソート機能まで先回りして実装します。
ステップ3:リファクタリング
テストが通ったまま、route.ts のエラーハンドリングを
共通の ApiError クラスに統一して。
リファクタリング後にテストを実行して、引き続き Green であることを確認して。
この3ステップが1サイクルです。次に「ブックマーク削除 API」を追加するなら、またステップ1に戻って新しいテストから始めます。