テスト戦略 レッスン6
カバレッジの正しい使い方
カバレッジ率が示すものと示さないものを理解し、数値に振り回されないテスト品質の考え方を学ぶ
カバレッジが示すもの・示さないもの
カバレッジ(coverage)とは、 テスト実行時にソースコードのどれだけの行・分岐が実際に実行されたかを示す指標です。 ただし「実行された」ことと「正しく検証された」ことは全くの別物です。
function divide(a: number, b: number): number {
return a / b;
}
// このテストはカバレッジ上は divide() の行を100%通過するが、
// 戻り値を全く検証していないため、バグを何も検出できない
it("divide を呼び出す", () => {
divide(10, 2); // 実行はされるが結果を確認していない
});このテストは「カバレッジ100%」に貢献しますが、divide(10, 0)でエラーになるかもしれない不具合も、 単純な計算間違いも一切検出できません。カバレッジは通過した行の割合であって、アサーション(検証)の質までは測っていません。
「カバレッジ100%が目的化する」罠
カバレッジ率を評価指標として厳格に運用すると、数値を上げること自体が目的化してしまい、 本来の目的である「バグを防ぐ」ことから外れてしまうことがあります。
起きがちな問題
- • アサーションのない「実行するだけ」のテストが量産される
- • カバレッジが低い箇所を無理に削って数値を上げる
- • 本当にリスクの高いロジックより、簡単にカバーできる行を優先してしまう
健全な使い方
- • カバレッジは「テストされていない箇所を見つける」ための地図として使う
- • ビジネス上重要なロジックを優先してテストする
- • 数値目標より「変更に自信を持てるか」を判断基準にする
実装詳細ではなく振る舞いをテストする
カバレッジ稼ぎのために内部のプライベート関数を無理やり公開してテストすると、 リファクタリングのたびにテストが壊れます。公開インターフェース(外から呼べる関数)を通じて 振る舞いを検証することで、内部構造を変えてもテストは壊れにくくなります。
// 悪い例: 内部実装の詳細(プライベートなヘルパー関数)に依存したテスト
import { _internalRoundToTwoDecimals } from "./pricing"; // 本来非公開の関数
it("内部の丸め関数が正しく動く", () => {
expect(_internalRoundToTwoDecimals(1.005)).toBe(1.01);
});
// 良い例: 公開されたインターフェース経由で振る舞いを検証する
import { calculateFinalPrice } from "./pricing";
it("税込み価格が正しく丸められて返る", () => {
expect(calculateFinalPrice(1000, 0.1)).toBe(1100);
});後者であれば_internalRoundToTwoDecimalsの実装方法を変えても、 外部から見た振る舞いさえ変わらなければテストは壊れません。
ポイント
- • カバレッジは「実行された割合」であり「正しく検証された」ことは保証しない
- • カバレッジ100%を目的化すると、意味のないテストが量産される罠がある
- • カバレッジは「テストの抜け漏れを探す地図」として活用する
- • 実装詳細ではなく公開インターフェース経由で振る舞いを検証する
確認クイズ
1 / 3テストカバレッジが示すものとして正しい理解はどれか?