AI エージェントにテストを先に書かせてから実装させるワークフロー:Claude Code・Codex・Gemini CLI での指示の出し方
コーディングエージェントに「失敗するテストを先に書き、通るまで実装する」手順を守らせるための指示の書き方を解説します。コンテキストファイルへの記述例、Claude Code の hooks やスキルによる強制、テストを改ざんさせないための注意点を紹介します。
目次
コーディングエージェントに「機能を実装して」と頼むと、実装は速いものの、動作確認が「たぶん動きます」で終わることがあります。これを防ぐ最も効果的な方法が、テストを先に書かせ、失敗を確認してから実装させるワークフローです。
エージェントは「テストを通す」という明確なゴールがあると、試行錯誤を自分で回せます。この記事では、3 つのツールで共通して使える指示の書き方と、ツール固有の強制方法をまとめます。
KEY POINT
この記事で分かること
- テスト先行ワークフローの 4 ステップと、各ステップの指示文
- コンテキストファイル(CLAUDE.md / AGENTS.md / GEMINI.md)への記述例
- テストの改ざんを防ぐ設定と、うまくいかないときの対処
ワークフローの 4 ステップ
| ステップ | 内容 | エージェントへの指示 |
|---|---|---|
| 1. テストを書く | 仕様に基づく失敗するテストを書く | 「実装はまだ書かないで」を明示 |
| 2. 失敗を確認する | テストを実行し、失敗することを確認する | 失敗の理由が「未実装」であることを確認させる |
| 3. 実装する | テストが通るまで実装を修正する | 「テストは変更しない」を明示 |
| 4. 整理する | 重複除去、命名の改善、テストの追加 | リファクタリング後もテストが通ることを確認させる |
ポイントは、ステップ 1 と 3 を別の指示として分けることです。「テストを書いて実装して」と 1 つの指示にすると、エージェントは実装を先に書き、それに合わせたテストを後付けしがちです。
指示文の例
ステップ 1:テストだけ書かせる
`src/lib/pricing.ts` に、数量と単価から割引後の合計を返す calculateTotal 関数を追加したい。
仕様:
- 10 個以上で 5% 引き、100 個以上で 10% 引き
- 数量が 0 以下なら例外を投げる
- 端数は切り捨て
まず `src/lib/pricing.test.ts` にこの仕様を満たすテストだけを書いてください。
実装はまだ書かないでください。テストを書いたら `pnpm test pricing` を実行し、
すべて失敗する(関数が存在しない)ことを確認して報告してください。
ステップ 2〜3:実装させる
pricing.test.ts のテストがすべて通るように、calculateTotal を実装してください。
テストファイルは変更しないでください。テストが不適切だと思う場合は、
変更せずに理由を報告してください。
実装後に `pnpm test pricing` を実行し、結果を貼ってください。
ステップ 4:整理させる
実装とテストを見直して、重複や分かりにくい命名があれば整理してください。
境界値(9 個、10 個、99 個、100 個)のテストが不足していれば追加してください。
最後に `pnpm test pricing` と `pnpm lint` を実行して結果を報告してください。
用語解説
境界値テスト: 仕様の切り替わる境目(10 個、100 個)とその前後(9 個、11 個)を確認するテストです。エージェントは「代表的な値」だけをテストしがちなので、境界値の追加は明示的に指示すると効果があります。
コンテキストファイルに書いておく
毎回指示するのが面倒なら、コンテキストファイルにワークフローを書いておきます。3 ツールとも同じ内容で通用します。
## 実装の進め方(テスト先行)
1. 新しい機能や修正では、先に失敗するテストを書き、実行して失敗を確認する
2. 実装はテストが通るまで行う。テストファイルは実装中に変更しない
3. テストが不適切だと判断した場合は、変更せずに理由を報告して指示を待つ
4. 完了報告には、実行したテストコマンドと結果(成功 / 失敗数)を含める
5. テストの実行環境がない場合は、最初にセットアップを提案する
CLAUDE.md、AGENTS.md、GEMINI.md の共通化については CLAUDE.md・AGENTS.md・GEMINI.md の違いと共通化テクニック を参照してください。
Claude Code:hooks とスキルで強制する
Claude Code では、指示ベースではなく仕組みで強制できます。
実装フェーズ中のテストファイル編集をブロックする hook(PreToolUse):
#!/usr/bin/env bash
# .claude/hooks/protect-tests.sh
# 環境変数 PROTECT_TESTS=1 のときだけテストファイルの編集を禁止する
[ "${PROTECT_TESTS:-0}" = "1" ] || exit 0
file=$(jq -r '.tool_input.file_path // empty')
case "$file" in
*.test.*|*.spec.*|*/__tests__/*)
echo "実装フェーズではテストファイルを変更できません: $file。テストが不適切なら理由を報告してください。" >&2
exit 2 ;;
esac
exit 0
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [{ "type": "command", "command": "bash .claude/hooks/protect-tests.sh" }]
}
]
}
}
PROTECT_TESTS=1 claude で起動すれば、実装フェーズ中のテスト改ざんを物理的に防げます。hooks の基本は hooks で lint と format を自動実行する を参照してください。
スキルで手順を固定する: /tdd 仕様 のようなスラッシュコマンドに 4 ステップを書いておくと、毎回同じ手順で進みます(スラッシュコマンドの作り方)。テスト実行を サブエージェント に任せると、長いテスト出力でメインのコンテキストを消費しません。
Codex・Gemini CLI での工夫
Codex と Gemini CLI には hooks に相当する仕組みがないため、指示と確認で運用します。
- Codex: 実装フェーズは
--sandbox workspace-writeのまま、/diffでテストファイルに変更がないことを確認してから承認する。AGENTS.mdにワークフローを書く。 - Gemini CLI: チェックポイント機能を有効にしておき、テストファイルが変更されていたら
/restoreで戻す。GEMINI.mdにワークフローを書く。
うまくいかないときの対処
| 症状 | 対処 |
|---|---|
| テストを書く段階で実装まで書いてしまう | 「実装はまだ書かない」を指示の最後にもう一度書く。Claude Code なら Plan Mode で計画だけ出させる |
| テストが通らないとテストを緩める | 「テストは変更しない」を明示し、hooks でブロックする |
| モックだらけで意味のないテストになる | 「外部依存はモックしてよいが、対象関数のロジックはモックしない」と指示する |
| テスト実行に時間がかかりすぎる | 対象を絞るコマンド(pnpm test pricing)を指示に含める |
まとめ
- 「テストを書く」と「実装する」を別の指示に分け、間に失敗の確認を挟む
- 「テストファイルは変更しない」「完了報告にテスト結果を含める」を毎回明示するか、コンテキストファイルに書く
- Claude Code は hooks でテストファイルの編集をブロックし、スキルで手順を固定できる
- Codex・Gemini CLI は
/diffやチェックポイントで、テストが改ざんされていないか確認する
よくある質問
- AI にテストを書かせると、実装に合わせた意味のないテストになりませんか?
- 実装より先にテストを書かせ、そのテストが失敗することを確認させると、実装に引きずられたテストになりにくくなります。指示の順序が重要です。
- AI がテストを通すためにテスト自体を書き換えることはありますか?
- あります。「テストは変更しない」と明示し、Claude Code なら hooks でテストファイルの編集をブロックすると防げます。
- 既存のテストがないプロジェクトでも使えますか?
- 使えます。最初に「テストの実行環境を整えて」と頼み、1 本目のテストが動く状態を作ってから始めてください。
参考にした一次情報
この記事は公式ドキュメントを基に AI が下書きを作成し、運営者が内容を確認して公開しています。誤りを見つけた場合はお問い合わせからお知らせください。