Claude Media
Claude Codeに再現テストを先に書かせてバグを直す — 回帰テストを残す手順

Claude Codeに再現テストを先に書かせてバグを直す — 回帰テストを残す手順

バグ修正は、失敗する再現テストを先に書かせ、修正後に通ること、修正を戻すと再び落ちることまで確かめると回帰テストとして残ります。指示文とレビュー観点をまとめます。

Claude Codeに再現テストを先に書かせてバグを直す — 回帰テストを残す手順

バグ修正で先に頼むのは修正ではなく、バグを再現する失敗テストです。Claude Codeのベストプラクティスにも、「ログインのバグを直して」という頼み方の改善例として「失敗するテストを書いて再現し、それから直す」という指示が載っています。再現テストが赤になり、修正後に緑になり、修正を戻すとまた赤になる。この3点が揃って初めて、そのテストは回帰テストとして残せます。

なぜバグ修正は「再現テストが先」なのか

Claudeは作業が終わったように見えた時点で止まります。通るか落ちるかを返す確認手段が手元になければ、「終わったように見える」ことしか判断材料がありません。公式のベストプラクティスは、これを「人間がテストスイートの代わりになってしまう」状態と表現しています。

バグ修正では、この問題が特に出やすくなります。症状の説明だけで修正を頼むと、Claudeは原因を推測して直し、推測が外れても気づく手段がありません。再現テストを先に書かせると、次の3つが手に入ります。

効果

再現テストを先に置く効果

  • 原因の仮説を検証できる

    テストが症状と同じ形で落ちれば、原因の見立てが当たっている目安になります。

  • 修正の完了条件が決まる

    赤いテストを緑にする、という合否つきの終了条件ができます。

  • 同じバグの再発を検知できる

    修正後もテストが残るので、将来の変更で再発すれば落ちます。

新機能の失敗テスト先行やStop hookでの締め方はClaude Codeでテスト駆動開発(TDD)を回す手順が扱っています。本稿はバグ報告から始まる場面に絞り、「落ちるべき理由で落ちているか」と「修正を戻すと落ちるか」の確かめ方に重点を置きます。

手順:再現テストから回帰テストまで

手順

バグ起点の回帰テスト運用の流れ

  1. 1

    症状と再現条件を渡す

    バグ報告の文面、エラー出力、再現コマンドをそのまま渡します。バグ修正の節にも、再現コマンドと再現手順、毎回起きるか散発かを伝えると良いと書かれています。

  2. 2

    失敗するテストだけを書かせる

    この段階では修正に触れさせません。テストを実行させ、失敗メッセージを報告させます。

  3. 3

    失敗の理由を症状と照合する

    失敗メッセージが報告された症状と一致するかを自分の目で見ます。別の理由で落ちているなら、それは再現テストではありません。

  4. 4

    テストを固定して修正させる

    テストファイルを変えずに本体だけ直させ、同じテストが通るところまで進めます。

  5. 5

    修正を戻してテストが落ちることを確かめる

    修正だけを一時的に外し、テストが再び赤になるか確認します。赤にならなければ、そのテストはバグを捕まえていません。

手順1〜2:再現テストを書かせる指示文

ベストプラクティスの改善例は、ファイルの場所、症状、期待する状態をあらかじめ伝える形です。この例を土台に、修正を後回しにする制約を足した指示文は次のようになります。

ログインがセッションの期限切れ後に失敗するという報告があります。
src/auth/ のトークン更新処理を見て、この症状を再現する失敗テストを
tests/auth/ に1つだけ追加してください。
まだ本体のコードは変更しないでください。
テストを実行し、実行コマンドと失敗メッセージをそのまま報告してください。

「本体のコードは変更しない」と「失敗メッセージをそのまま報告」の2点が要です。後者は、ベストプラクティスが勧める「Claudeに証拠を出させる」使い方に沿っています。実行したコマンドとその出力を求めておくと、「テストは失敗しました」という要約を信じる必要がなくなります。

手順3:落ちる理由を自分で読む

再現テストで最も多い失敗は、バグとは別の理由で落ちているケースです。次の表は、報告された症状が「期限切れ後にログインが失敗する」場合の読み分けの例です。

失敗メッセージの傾向読み方次の一手
症状と同じ値や例外で落ちる読み方再現できている可能性が高い次の一手手順4へ進む
importや未定義の関数でエラーになる読み方テストが書けていない。再現ではない次の一手テストの足場を直させる
期待値が症状と無関係な箇所で食い違う読み方別の不具合か、期待値の誤り次の一手期待値の根拠を説明させる
最初から通る読み方バグを捕まえていない次の一手再現条件を見直させる

テストが最初から通る場合、Claudeにそのまま修正を続けさせても、直す対象が見つかりません。再現条件(時刻、データ、並び順)を具体的に言い直して書き直させます。

手順4:テストを固定して直させる

再現テストが症状どおりに落ちたら、次の指示で修正に進みます。

追加したテストは変更しないでください。
src/auth/ の本体だけを修正して、そのテストが通るまで進めてください。
最後に tests/auth/ 全体を実行して、他のテストが壊れていないことも確認してください。

「テストは変更しない」と明示するのは、通すためにテスト側の期待値を書き換える近道を封じるためです。実装の側でテスト入力を決め打ちして通す問題はClaudeがテストだけ通すハードコードを防ぐプロンプトの書き方に詳しくあります。

手順5:修正を戻して、再び落ちることを確かめる

ここが回帰テストになるかどうかの分かれ目です。テストと修正を別のコミットに分けておくと、確認は1コマンドで済みます。<修正コミット> は、本体の修正だけを含むコミットのハッシュに読み替えてください。

git add tests/auth/ && git commit -m "test: セッション期限切れ後のログイン失敗を再現"
git add src/auth/ && git commit -m "fix: 期限切れトークンの更新処理を修正"
 
# 修正コミットの変更だけを作業ツリーに逆適用し、テストを実行する
git revert --no-commit <修正コミット>
npm test -- tests/auth/    # 失敗するはず
git revert --abort         # 逆適用を取り消して元に戻す

npm test の部分はプロジェクトのテストコマンドに置き換えます。修正を外した状態でテストが落ち、元に戻すと通る。この往復を確認して初めて、テストが修正の有無を判別していると言えます。

この確認自体もClaudeに任せられます。ただし「修正を戻す」操作はファイルを書き換えるため、Claudeに頼むときは戻す範囲と、確認後に git revert --abort で必ず元へ戻すことを指示に含めます。

指示を毎回書かないために:CLAUDE.mdに回帰テストの型を置く

バグ修正のたびに同じ制約を書くなら、CLAUDE.mdに置くと省けます。リポジトリにテスト手段があるなら、その実行コマンドがCLAUDE.mdで最も有用な1行です。そこに再現テストの規約を足す形です。

# テスト
- テスト実行: npm test(単一ファイル: npm test -- <パス>)
 
# バグ修正
- バグ修正では、先に失敗する再現テストを書き、失敗メッセージを報告する
- 再現テストを書き終えるまで本体のコードを変更しない
- 修正後は、再現テストを変更していないことを確認してから完了と報告する

ただし、CLAUDE.mdは指針であり、Claudeが「今回は当てはまらない」と判断する余地が残ります。毎回必ず守らせたいことはフック、拒否ルール、管理設定に移すようベストプラクティスは勧めています。再現テストを書かずに終わらせたくないなら、Stop hookで「テストが通らない限りターンを終えない」ところまで機械化できます。

ベストプラクティスのStop hookの例は、型チェックが通るまでClaudeを止める設定です。同じ形でテストコマンドに置き換えた例を次に示します(置き換えた例なので、コマンドはプロジェクトに合わせます)。

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          { "type": "command", "command": "npm test 1>&2 || exit 2" }
        ]
      }
    ]
  }
}

終了コード2がターンの終了を止め、標準エラーに出た内容が「まだ終われない理由」としてClaudeに戻ります。連続8回止めても進展がなければClaude Code側がブロックを解いてターンを終えます。上限は CLAUDE_CODE_STOP_HOOK_BLOCK_CAP で変えられます。

注意点が1つあります。このフックは全体のテストが通ることを条件にするだけで、「再現テストが追加されたか」までは見ません。再現テストの有無は、次のレビューで人が見ます。

レビューで見る5つの観点

Claudeの報告を受けたら、差分とテスト出力を次の観点で見ます。

回帰テストのレビュー観点

  • 再現テストは、報告された症状と同じ理由で失敗しているか(失敗メッセージの原文で確認)
  • 修正のコミットで、再現テストのファイルが書き換えられていないか
  • 修正を外すとテストが落ち、戻すと通ることを確認したか
  • テストが、バグを避けるモックや決め打ちの入力で通っていないか
  • 修正が、バグの原因以外の範囲まで広がっていないか

最後の観点には、別のコンテキストからのレビューが役に立ちます。公式は、ターミナルの /code-review が会話を見ていない新しいサブエージェントで差分を確認し、コードを書いた経緯の推論に引きずられないと説明しています。仕様に沿った確認をさせたい場合のレビュー指示も紹介されています。

use a subagent to review the rate limiter diff against SPEC.md, check every requirement is implemented and the listed edge cases have tests, report gaps that affect correctness and skip style

この例では skip style を外さないよう注意書きがあります。問題探しを頼まれたレビュアーは必ず何かを見つけるため、指摘をすべて直すと、起きえないケースのテストや余分な抽象化が増えるからです。バグ修正のレビューでも、「再現テストと修正の対応」を見る範囲に絞った指示にしておくと、差分が膨らみにくくなります。自分で書いた差分をPRとして観点別に見たいときはpr-review-toolkitの専門エージェント構成が使えます。

テスト追加が過剰になる問題はClaude Fable 5.1の余計な修正とテスト追加を抑える指示文にまとめています。

よくあるつまずき

落とし穴

再現テストが最初から通ってしまう

再現条件が足りず、バグの起きる経路を通っていません。そのまま修正に進むと、Claudeは根拠のない修正を入れかねません。時刻、データの並び、設定値など、報告に含まれる条件をテストの入力として具体的に言い直させます。

落とし穴

修正を外してもテストが落ちない

テストが修正対象とは別の経路を見ています。落ちるまでテストを書き直させ、書き直し後にもう一度、手順5を実施します。ここを飛ばすと、将来の再発を検知できないテストが残ります。

落とし穴

散発的に起きるバグで、テストが不安定になる

再現が運任せだと、赤と緑が実行ごとに入れ替わります。バグ修正の節には、エラーが毎回起きるか散発的かをClaudeに伝えるよう書かれています。時刻や乱数、並行処理を固定できる形にテストを作り直させ、固定できない場合は無理に回帰テスト化せず、ログの追加や監視で補います。

落とし穴

報告の要約だけで完了にしてしまう

「テストは通りました」という一文は、確認手段になりません。実行したコマンドと出力を求めるのが勧められており、見ていないセッションでは、それが唯一の証拠になります。

まとめ

バグ修正をClaude Codeに任せるときは、「直して」の一言を、再現テスト、修正、修正を外す確認の3段に分けて渡すと、完了の判断がテストの出力に移ります。テストと修正を別コミットにしておけば、最後の確認は1コマンドです。毎回の指示はCLAUDE.mdに、必ず守らせたい部分はStop hookに寄せ、レビューでは失敗の理由と修正の範囲を見ます。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/best-practices
  2. code.claude.com/docs/en/common-workflows
この記事を共有:XはてブLinkedIn