Claude Media
Claude Codeでテストから仕様を読む — 壊して裏を取る手順

Claude Codeでテストから仕様を読む — 壊して裏を取る手順

仕様書が無いモジュールのテスト名を「入力→期待結果」の表にし、わざと壊して落ちるか確かめる手順。テストが仕様を表すのか空洞なのかを見分けます。

参加直後のリポジトリに仕様書が無いとき、いちばん近い代わりになるのはテストです。ただしテストは「書いてあること」しか語らず、「書いていないこと」は語りません。しかも、緑色のテストが仕様を守っているとは限りません。

この記事では、Claude Codeに対象モジュールのテストを読ませて「入力 → 期待結果」の表にし、その表の各行を実際に壊して確かめる流れを組みます。読む段階で終わらせず、テスト実行まで通して裏を取るのが要点です。テストが無いコードに書く側の話はClaude Codeで特性テストを書くにあります。ここで扱うのは、すでにあるテストを読む側です。

テスト名の一覧は仕様の下書きになる

テストの関数名は、書いた人が「この挙動を守りたい」と思った箇所の目録です。test_free_shipping_at_threshold や test_negative_quantity_raises のような名前が並んでいれば、中身を読む前に仕様の輪郭が見えます。

下書き止まりなのは、名前と中身がずれることがあるからです。名前は「境界値で送料無料」と言っているのに、実際の入力は境界から遠い値で、どの実装でも通ってしまう。この種のずれは、名前を読むだけでは見つかりません。そこで読む工程を3つに分けます。

手順

テストから仕様を取り出す流れ

  1. 1

    名前を読んで箇条書きにする

    テスト名と、あればdocstringから、挙動を1行ずつ書き出します。

  2. 2

    中身を読んで入力と期待値の表にする

    各テストが実際に渡している入力と、検証している値を拾います。名前と食い違う行に印を付けます。

  3. 3

    壊して落ちるか確かめる

    表の行ごとに、その挙動を壊す変更を1つ入れてテストを回します。落ちれば仕様として機能しており、落ちなければ空洞です。

Claude Codeに読ませるプロンプト

起動したら、まず大きく掴ませるのが公式の入門のやり方です。common-workflowsの「Understand new codebases」は、全体の概要を聞き、アーキテクチャーのパターンやデータモデルへ絞っていく順を示し、プロジェクト固有の用語集を頼むことも勧めています。テストを読ませる前に、この用語集を取っておくと、期待値に出てくる業務用語の意味で迷いません。

そのうえで、対象を1モジュールに絞って読ませます。例として、送料計算の shipping/fee.py とそのテストを読む場面を想定します。

shipping/fee.py の仕様書は無い。tests/test_shipping_fee.py だけを読んで、
次の表を作って。コードの実装は読まないで。
列: テスト名 / 渡している入力 / 検証している期待結果 / 名前と中身が食い違う点
最後に、テストが触れていない入力の種類を箇条書きにして。

「実装は読まないで」と言うのが肝です。実装を先に読ませると、Claudeは実装から仕様を逆算して表を埋め、テストが何を語っているのかが混ざります。読む対象を絞る指示は、best-practicesが示す「具体的に頼む」書き方と同じ方向です。公式の例では「fix the login bug」と曖昧に頼む代わりに、場所と再現テストまで指定しています。

返ってくる表は、例えば次のような形になります(例示であって、実際の出力ではありません)。

テスト名入力期待結果食い違い
test_free_shipping_at_threshold入力小計5000期待結果送料0食い違い名前は境界値だが入力が5000ちょうどか要確認
test_fee_for_island入力小計3000、離島期待結果送料1200食い違いなし
test_negative_quantity_raises入力数量 -1期待結果ValueError食い違いなし
test_coupon_applied入力クーポン付き期待結果送料0食い違い割引の順序は未検証

「触れていない入力」の欄が、表の価値を決めます。例えば「小計0」「複数クーポン」「離島かつ送料無料」が並んだとしたら、それは仕様が決まっていないのではなく、テストが語っていない領域です。ここを仕様として断言させないでください。

表の各行を壊して裏を取る

表ができたら、各行を実装側から壊します。これがミューテーションテストの手作業版です。専用ツールを入れなくても、行ごとに1か所を変えて回すだけで、同じ判断ができます。

best-practicesは、Claudeに「合否が返るもの」を渡すと自分で直しては回し直すループに入ると説明しています。ここではそのループを、直すためでなく壊すために使います。

表の各行について、その挙動を壊す変更を shipping/fee.py に1つだけ入れ、
pytest を実行して、落ちたテスト名を記録して。1行ごとに git restore で戻すこと。
壊す例: 比較演算子の >= を > に変える / 定数を1だけずらす / 例外を握りつぶす。
最後に「壊しても全部通った行」だけを一覧にして。

作業は専用ブランチで行い、各行のあとにソースを元に戻させます。戻し忘れが次の行の結果を汚すからです。確認は自分でも一度見ておくと安全です。

git status --short          # 変更が残っていないこと
git diff --stat             # 壊した変更が戻っていること

公式は、完了を伝えられたら「実行したコマンドとその出力」という証拠を求めるよう勧めています。理由は、「テストが通りました」という一文だけでは情報が少なく、出力を読むほうが再実行より速いからです。壊す作業でも同じで、行ごとに落ちたテスト名まで出させます。

結果は3通りに分かれます。

判定

壊した結果の読み方

  • 期待どおりのテストが落ちた

    その行は仕様として機能しています。表の記述を仕様書に採用できます。

  • 別のテストだけが落ちた

    名前と中身がずれています。表の行を書き直し、落ちたテストの名前で仕様を読み直します。

  • 全部通った

    空洞です。その挙動は、テストに守られていないので、仕様としては未確定です。

空洞なテストの見分け方

壊しても通る行には、型があります。

  • 境界から遠い入力: 閾値のテストなのに、5000の代わりに100000を使っている。>= と > の差が出ません。
  • 期待値を実装と同じ式で計算している: テスト内で fee = subtotal * 0.1 と書いて比較している。実装を変えたらテストも同じ式を使うため、同時にずれて通り続けます。
  • 例外の型を見ていない: pytest.raises(Exception) だけで、どの例外かを問うていない。
  • モックが結果を返している: 検証したい処理そのものがモックに置き換わっている。

最後の型は、Claudeが書いたテストでも起きます。実装に合わせて期待値を書き換える癖への対処は、テストのハードコードを防ぐプロンプトで扱っています。

読む側で押さえたいのは、空洞を見つけたときの扱いです。そのテストを根拠に「仕様はこうです」と書かず、仕様表に「未確定」と明記して残します。common-workflowsの「Work with tests」は、Claudeに境界条件のテストを足させる流れを示しています。空洞な行は、その入口として使えます。ただし書き足す前に、現状の挙動を先に記録する手順が要るので、そこは特性テストの側に渡します。

仕様表を残す

作った表は、会話が終わると消えます。リポジトリに置いておくと、次に同じモジュールを触る人とClaudeが使えます。

# shipping/fee.py の仕様(テストから抽出・壊して確認済み)
 
| 挙動 | 根拠のテスト | 状態 |
|---|---|---|
| 小計 5000 以上で送料 0 | test_free_shipping_at_threshold | 確認済み |
| 離島は 1200 | test_fee_for_island | 確認済み |
| クーポンと離島の併用 | なし | 未確定 |

CLAUDE.md には、この表の場所と、検証コマンドだけを書きます。best-practicesは CLAUDE.md に置くものとして「リポジトリで作業の完了を確かめる方法(テストのコマンド、1ファイルだけ回す方法)」を挙げています。仕様表そのものを CLAUDE.md に貼ると長くなるので、参照先だけにします。

- 送料の仕様: docs/spec/shipping-fee.md(未確定の行は変更しない)
- 1ファイルだけ回す: pytest tests/test_shipping_fee.py

表が語れないこと、途中でつまずく点

表から出せる仕様は、テストが書かれた範囲に限られます。ここを取り違えると、仕様書のつもりで誤解を固めます。

  • テストが古い挙動を守っていることがある: 仕様変更のあとに直されなかったテストは、いまの運用と食い違ったまま緑です。壊して落ちる行でも、「正しい仕様」とは限りません。業務の人に確かめる行として、表に印を付けておきます。
  • バグを守るテストもある: 税の端数処理のように、不具合を含んだ値がそのまま期待値になっている場合です。気づいたら仕様表に「現状の挙動(要確認)」と書き、直す判断は別に切り出します。
  • 壊した変更が保存されたまま残る: 行ごとの git restore を忘れると、次の行の結果が前の破壊と混ざります。作業前にコミット済みの状態から始め、行ごとに git status --short の出力が空になるのを見てから次へ進みます。未コミットの自分の変更があるなら、先に退避してください。
  • 遅いテストと不安定なテスト: 1行ごとに全体を回すと時間がかかります。common-workflowsの「Work with tests」が示すとおり、Claudeは既存のテストの書き方に合わせて動くので、対象テストファイルだけを回す指示を足します。落ちたり通ったりを繰り返すテストは、壊した結果の判定に使えません。不安定なテストの調べ方で先に原因を切り分けます。

読み切れないときの分担

モジュールが大きく、テストファイルが数百行に及ぶなら、読む作業をサブエージェントに出す手もあります。best-practicesは、長い出力を主会話に持ち込まない目的で調査をサブエージェントに任せる使い方を挙げています。テスト一覧の抽出だけを任せ、壊して確かめる工程は主会話で見届ける、という分担が扱いやすいでしょう。

大規模なリポジトリで読む範囲を決める考え方は大規模コードベースの探索のコツにまとめています。言語が古く、テストも無い読解の例はPerlの古いスクリプトを読解し移行テストを作るが参考になります。

まとめ

テスト名は仕様の目次で、壊して落ちた行だけが本文です。落ちなかった行を「未確定」として残せば、その後の変更で何を足すべきかも見えます。まず1モジュール、表を5行ほど作って、全行を壊してみるところから始めてください。

参照した一次ソース

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

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