CTS-KB

テスト品質・網羅性編 — カバレッジは「上げる」前に「正しく測る」。偽の穴・計測盲点・1,124 行の隠れコピペ【実践編 第7章】

⏱ 約 7 分で読めます
#ステアリング駆動開発#テストカバレッジ#テスト品質#TDD#技術的負債#CI/CD#Python#.NET

📖 はじめに — カバレッジの数字は、放っておくと嘘をつく

テストカバレッジほど「あるのに信用できない」メトリクスは少ない。数字が高くても、計測から丸ごと抜けている領域があれば意味がないし、ロジックのない DTO が分母に混ざっていれば実態より低く見える。AI コーディングはテストも量産してくれるが、「どこを測れていないか」は誰も教えてくれない

本章は 2 本のステアリングの実録だ。前半は Python ジョブ群のカバレッジを 96% 一律ゲートに引き上げた話、後半は .NET / フロントエンドの計測そのものの負債を解消した話。共通する結論を先に言う — カバレッジは「上げる」前に「正しく測る」が先である。

🐍 前半: Python ジョブ群を 96% ゲートへ

計画の数字は腐る — Plan 72% vs 実測 29%

Python ジョブ群のカバレッジはジョブごとにバラバラ(ベースライン実測で 7%〜82%)で、CI ゲートも一律ではなかった。是正ステアリングの起票にあたり、まず全ジョブを再実測したところ、いきなり型 3(定量が起票を正当化する)の教材が出た。

計画文書ではあるファイル(SAGA キャンセル監視、約 140 行)のカバレッジを 72% としていたが、実測は 29%43 ポイントの乖離があった。計画起案から着手までの数週間で、周辺テストの整理により数字が大きく動いていたのだ。教訓は単純で、着手時に必ず再実測する。計画時の数字を信じて見積もると、+18pt のつもりが +61pt の作業になる

達成値と、その過程で出た「本当の発見」

最終的に全 5 ジョブが 96% ゲートを通過した(96.01%〜99.54%)。だが本章的に重要なのは数字ではなく、その過程で別エージェントによるテストレビュー(test-reviewer) が発見したものだ。

  • 指摘 19 件中 9 件を即時対応、10 件は別 PR に分離 — レビュー指摘を「全部いま直す」と PR が肥大するため、対応とスコープ分離を明示的に裁いた
  • 網羅性の強化は具体的だった。例: ステータス 6 値 × 大文字小文字差を parametrize で 15 ケース × 2 ジョブ = 30 ケースに展開
  • そして最大の発見 — SAGA キャンセル監視の production コード 140 行と、そのテスト 422 行が、2 つのジョブ間で完全コピペ。合計約 1,124 行の重複が、カバレッジ作業をするまで誰にも認知されていなかった

この 1,124 行への対処が、スコープ規律の見本になった。共通化はカバレッジ回復ステアリングの目的ではないので、その場では直さず、実測値つきで別起票。テストを書くために対象コードを精読する行為は、それ自体が最強のコードレビューであり、型 2(ゼロベースライン確認が兄弟負債を炙り出す)が Python でも発動した形だ。

🔬 後半: 「測れていない」を潰す — .NET / フロントエンドの計測負債

後半のステアリングは、カバレッジレポートの分析から出発して計測自体の負債を 3 種類に腑分けした。ここが本章の目玉だ。

🔬 後半: 「測れていない」を潰す — .NET / フロントエンドの計測負債

実ロジックあり

ロジック無し DTO/record: 22 個

未実装スタブ Handler: 6 個

カバレッジ 0% のクラス 47 個

実体は何か?

TDD でテスト追加(真の穴)

ExcludeFromCodeCoverage で分母から除去(偽の穴)

カバレッジ負債でなく実装負債として別起票

発見 1: BC が丸ごと計測から消えていた

カバレッジ設定を精査すると、ある BC(Category)が coverlet 設定と CI レポート設定の二重除外で、レポートから丸ごと消えていた。誰も意図を覚えていない「無自覚な穴」だ。

対処が興味深い。実体を調査すると、この BC は 6 ファイルで、実ロジックを持つのは 1 クラス(早期リターン分岐 1 つ)だけと判明。オーナー裁定で除外は維持し、代わりに除外理由を両設定ファイルのコメントと ADR に明文化、残る 1 クラスのテスト整備は別起票した。除外をやめて数字を「正しく」することもできたが、それは分母に大量の interface 定義を混ぜるだけ。「無自覚な穴」を「理由が書かれた管理された負債」に変換することが正解だった。

発見 2: 0% クラスの半分は「偽の穴」

カバレッジ 0% のクラスは 47 個あった。愚直に「全部テストを書く」と走り出す前に分類すると、22 個はロジックを持たない DTO / record だった。これらに振る舞いテストを書くのは無意味どころか有害 — アサーションのない「実行するだけテスト」の温床になる。[ExcludeFromCodeCoverage] で計測対象外にし、分母を健全化した。

残る 0% のうち 6 個は未実装のスタブハンドラで、これは「テストがない」のではなく「実装がない」。カバレッジ負債ではなく実装負債として別の台帳に載せた。同じ 0% でも、意味が 3 通りある — 分類してから手を動かすと、無駄なテストを 22 クラス分書かずに済む。

発見 3: 計測ツール自体も疑う

フロントエンドでは、集計スクリプトの Lines 指標が内部データの癖で 99.8% と過大表示されることが判明し、Statements% を正とする判断も入った。ゲートの根拠になる数字は、その数字を作る道具から検証する第6章の「無スタイル DOM を検査していた」と同型の、計測の真正性の問題である。

確定値と ratchet — ゲートは「今の実力 − 誤検知余裕」から始める

健全化後の確定値は .NET が line 95.5% / branch 86.7%、フロントエンド統合が statements 95.0% / branch 86.5%。CI ゲートの閾値は実測より少し下(例: branch 85)から始め、85 → 88 → 90 と段階的に引き上げる ratchet 方式を採る。

最初から理想値でゲートを張ると、無関係な PR が誤検知で止まりゲートごと無効化される — 第6章の a11y 52 件を todo リスト化したのと同じ判断だ。ゲートの敵は甘さではなく、狼少年化である。

🧾 まとめ

  • 計画時のカバレッジ値は腐る。着手時に再実測してから見積もる(72% のつもりが実測 29%)
  • テストを書く過程は精読であり、隠れた重複 1,124 行のような兄弟負債を炙り出す。直すかどうかはスコープ規律で裁き、別起票をためらわない
  • カバレッジ 0% には 3 つの意味がある — 真の穴(テストを書く)・偽の穴(DTO は分母から除去)・実装負債(別台帳)。分類が先、着手は後
  • 計測除外は「無自覚」が罪。理由を設定ファイルと ADR に明文化して管理された負債にする
  • ゲートは実測 − 余裕から始めて ratchet で引き上げる。誤検知でゲートを狼少年にしない

テストの「量」を守る仕組みができた。次章は、そのテストが守るべき Python コードの中身 — 握り潰された例外と量産されたスタブの話、Python ジョブ群編へ。

📚 シリーズ記事(ステアリング駆動開発・実践編)

序章

  1. AI 駆動開発が積み上げる技術的負債

第I部: 技術的負債ドメイン別の実録(総点検ガイド + 8 ドメイン・全 9 回)

  1. リファクタリング総点検ガイド — 7 つの観点と進め方
  2. DDD/SOLID/BC 編 — god class 一掃と境界の機械ガード
  3. SAGA 編 — 新アーキテクチャ挑戦と再発ゲート
  4. Atomic Design リファクタ編 — 47 page 新規移植
  5. Storybook × a11y 実機編
  6. テスト品質・網羅性編(本記事)
  7. Python ジョブ群編
  8. パフォーマンス編
  9. セキュリティ編

第II部: 検証エンジン(クロスファミリー検証・全 6 回)

  1. 総論 — マルチ LLM の 2 系統と見取り図
  2. 裏取り編 — +18.1pt 論文の検証
  3. 設計編 — finder/verifier 分業と逆順禁止
  4. TDD×ハーネス編 — テスト保護と三層ゲート
  5. 実装編 — Claude Code の中から Codex を動かす
  6. モデル戦略編 — ティア割当と「買うか組むか」

終章

  1. クロスファミリー検証の最終型 —「正しく作る」から「自分で正しさを測り直す」へ — 検証エンジンに「計測」の層をはめ、自己修正ループを閉じる

番外編

  1. CI テスト 29 分 → 5.6 分 — 検証エンジンの平時運用実録 — 実測が有力仮説を殺し、退行前より速くなった 1 日
  2. GPT-5.6 sol 切替の当日実録 — もう一つのデフォルト追従 — CLI 更新が黙って替える finder と、当日中の再計測
  3. 遊休 90% の枠に仕事を振る — Codex 上流調査と verifier バッチ化 — 逆順禁止の境界を ADR で確定し、読む仕事を遊休枠へ移した 1 日
  4. ハーネスが単一プロジェクトを卒業する — プラグイン化と二層配布 — 4 プロジェクト展開への切り出しと、version + SHA ピンによる非強制追従
  5. 69分で5リリース — Opus 5 当日対応が暴いた共通ハーネスの死角 — 新モデル対応を起点に、配布・文書・CI の回帰を二消費者で検出した 69 分

関連記事