概要
教科書的には、ホワイトボックステストは内部構造(命令・分岐)を見て、ブラックボックステストは入出力だけを見る。実務で効くのは、これをテストケースの導出元が実装か仕様かの違いとして捉え直すことで、さらに言えば効いてくるのは入力より期待値の導出元である。
実装コードからテストを作るな、ではなく、実装コードから正解を決めるなが正確なところになる。
詳細
網羅基準はどこまで上げても実装の中にいる
| 記号 | 呼び名 | 意味 |
|---|---|---|
| C0 | 命令網羅 | すべての命令を 1 回以上通す |
| C1 | 分岐網羅 | すべての分岐を真・偽の両方向に通す |
| C2 | 条件網羅 | 分岐を構成する個々の条件の真偽を組み合わせる |
C0 → C1 → C2 と厳しくしても、数えている対象はずっとソースコードに書かれたもの。C2 は「4 パターン全部」「短絡評価を踏まえた 3 パターン」「全条件文の全組み合わせ」と定義が揺れ、目盛りそのものが実装の書かれ方で決まる。仕様に書いてあって実装に書かれていない間違いは、網羅率をいくら上げても見つからない。
C0 と C1 の差は else のない if を素通りする経路に出る。coverage run --branch の Missing に出る 3->5 は「3 行目から 5 行目へ直接抜ける道を通っていない」という意味。
実測: 同じ 100% が 0% と 100% に割れる
運賃関数(65 歳以上がシニア)に age < 66 の off-by-one を仕込み、4 つのスイートを走らせた結果。
| スイート | 入力の導出元 | 期待値の導出元 | 分岐カバレッジ | バグ入り実装 | ミューテーションスコア |
|---|---|---|---|---|---|
| 手書き whitebox(3/8/30/70) | 実装の分岐 | 仕様 | 100% | 全件パス | 0/9 |
| 手書き blackbox(境界値) | 仕様の境界 | 仕様 | 100% | 65 で失敗 | 9/9 |
| エージェント(実装のみ渡す) | 実装の境界 | 実装 | 100% | 全件パス | 7/9(ベースライン赤で無効) |
| エージェント(仕様のみ渡す) | 仕様の境界 | 仕様 | 100% | 65 で失敗 | 9/9 |
- 分岐カバレッジは「その道を通ったか」しか見ず、分岐の境界が正しい位置にあるかは見ない。入力 30 をバグが露出する唯一の値 65 に差し替えてもカバレッジは同じ 100% のまま
- 境界が 1 ずれたことに気づけるのは、境界の値そのものを踏むテストだけ。各分岐の真ん中の代表値をいくつ並べても 0/9
- 実装だけを渡されたエージェントは境界値をきちんと選んだが、
(65, 1000)とバグをそのまま正解として書き写した。バグがあるあいだは全件パスし、バグを直すとそのテストが落ちる。テストがバグを守る側に回る
独立した 2 経路には情報がある
「仕様もエージェントと一緒に作って顧客確認も取ったのだから、実装は仕様どおりのはず」という反論は、確率の話であって独立性の話ではない。
- 仕様 → 期待値、仕様 → 実装 の 2 経路 → 食い違えばどちらかが間違っている
- 実装 → 期待値 の 1 経路 → 同じものを 2 回数えているだけで、何が起きても食い違わない
複式簿記が経理担当者を疑って導入されているわけではないのと同じで、エラー率がいくら低くても写した期待値の検出力はゼロのまま。顧客はコードを読まず仕様書を承認しているので、外部の検証を受けた唯一の成果物は仕様書であり、エージェント主導の開発ほど仕様がオラクルとして相対的に強くなる。
実際に壊れるのは、境界の翻訳(以上/未満の取り違え)、仕様が黙っている部分、ドリフト(仕様は 1 回書かれ、コードは何百回も編集される)、仕様変更の取りこぼし、同じモデルによる相関した誤解、の順。
崩れる条件は 1 つ。仕様書がコードから作られている場合(「この実装をドキュメント化して」)は、名前が仕様書でも独立性はゼロ。効いているのは成果物の名前ではなく導出の向きである。
エージェントにテストを書かせるときのルール
仕様と実装の両方を文脈に入れると、ルールの有無に関係なくエージェントは仕様を正解に採り、食い違いを報告してきた。決定要因は仕様が文脈に入っているか。実際のリポジトリでは仕様は docs/ の別ファイルにあり、エージェントが開くとは限らないので、ルールは「仕様を読ませる」ために書く。
## テスト生成のルール
- テストを書く前に、対象機能の仕様を `docs/specs/` から読むこと。
仕様が見つからない場合はテストを書かず、どこにあるか確認すること。
- 期待値は仕様の記述から導出すること。実装の戻り値を期待値の根拠にしない。
- 仕様と実装が食い違う場合、テストは仕様に従わせ、食い違いを報告すること。
- カバレッジ 100% を完了条件にしない。仕様のどの境界を踏んだかで報告すること。
重いのは 1 行目。2 行目だけ書いても、仕様がコンテキストに無ければ従いようがない。
ホワイトボックスの役割は残る
到達しないコード、死んだ分岐、仕様に書かれていない実装上の経路を洗い出すのは今もカバレッジの仕事。できないのは仕様適合の判定だけで、カバレッジ 100% を「テストが十分」と読み替えるところが誤りになる。入力は仕様の境界と実装の分岐の両方から集めるのが最善で、一方通行にすべきなのは期待値だけ。実装から期待値を取ったテストも、回帰検出の役には立つ。
関連ページ
- ミューテーションテスト — テストの検出力を測る道具とその罠
- 作る役と確かめる役の分離(maker-checker) — 実装の主体と期待値の主体を分ける
- BDD と Gherkin — 仕様を実行可能な形で書く
- 沈黙する失敗 — 緑のまま残るバグ
- AI エージェントにリファクタさせる時の完了の定義 — 指標を完了条件に据えると指標が最適化される