概要

テストを書いていると「カバレッジ何パーセントを目指すべきか」という議論に必ず遭遇します。しかし、カバレッジは「テストが通過したコードの割合」を示すだけで、テストの検証内容が正しいかどうかは教えてくれません。行カバレッジが 100% でも、分岐の片側しか通っていなかったり、アサーションが甘かったりすれば、バグはすり抜けます。この記事では、Maven プロジェクトに JaCoCo を導入してカバレッジを測定する手順を示したうえで、行カバレッジ・分岐カバレッジ・命令カバレッジそれぞれが何を測っているかの違いを具体的なコード例で説明します。カバレッジ率の現実的な目標設定、カバレッジだけに依存しないテスト品質の考え方にも触れます。

使いどころ

CI パイプラインに JaCoCo を組み込み、カバレッジが一定値を下回ったらビルドを失敗させるゲートを設定する

レガシーコードのテスト拡充で現状カバレッジを可視化し、優先的にテストすべき箇所を特定する

コードレビューでカバレッジレポートを参照し、分岐カバレッジが低い条件分岐のテスト追加を依頼する

プルリクエスト単位で新規追加コードのカバレッジを確認し、既存負債と切り分けて管理する

重要ロジックだけ PITest などと組み合わせて、単純な通過ではなく検証強度も見直す

コード例

DiscountServiceTest.java
import java.math.BigDecimal;
import java.math.RoundingMode;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import static org.junit.jupiter.api.Assertions.*;

class DiscountServiceTest {

    static class DiscountService {
        BigDecimal calculateDiscount(String rank, BigDecimal amount) {
            if (amount.compareTo(BigDecimal.ZERO) <= 0)
                throw new IllegalArgumentException("購入金額は正の値を指定してください");
            return switch (rank) {
                case "GOLD" -> amount.multiply(new BigDecimal("0.10"))
                    .setScale(0, RoundingMode.FLOOR).min(new BigDecimal("5000"));
                case "SILVER" -> amount.multiply(new BigDecimal("0.05"))
                    .setScale(0, RoundingMode.FLOOR).min(new BigDecimal("2000"));
                case "BRONZE" -> BigDecimal.ZERO;
                default -> throw new IllegalArgumentException("不明なランク: " + rank);
            };
        }
    }

    private DiscountService service;
    @BeforeEach void setUp() { service = new DiscountService(); }

    @Test @DisplayName("GOLD 10%割引")
    void gold10percent() {
        assertEquals(new BigDecimal("1000"), service.calculateDiscount("GOLD", new BigDecimal("10000")));
    }

    @Test @DisplayName("GOLD 上限5000円")
    void goldCap() {
        assertEquals(new BigDecimal("5000"), service.calculateDiscount("GOLD", new BigDecimal("100000")));
    }

    @ParameterizedTest
    @CsvSource({"10000,500", "40000,2000", "80000,2000"})
    @DisplayName("SILVER 5%割引(上限2000円)")
    void silver(String amount, String expected) {
        assertEquals(new BigDecimal(expected), service.calculateDiscount("SILVER", new BigDecimal(amount)));
    }

    @Test @DisplayName("BRONZE は割引なし")
    void bronze() { assertEquals(BigDecimal.ZERO, service.calculateDiscount("BRONZE", new BigDecimal("50000"))); }

    @Test @DisplayName("不明ランクで例外")
    void unknownRank() { assertThrows(IllegalArgumentException.class, () -> service.calculateDiscount("PLATINUM", new BigDecimal("10000"))); }

    @Test @DisplayName("金額0以下で例外")
    void zeroAmount() { assertThrows(IllegalArgumentException.class, () -> service.calculateDiscount("GOLD", BigDecimal.ZERO)); }
}

Java 8 / 17 / 21 の完全なサンプルコードは GitHub リポジトリ で確認できます。

Version Coverage

sealed class や record のカバレッジには JaCoCo 0.8.8 以上が必要。

Java 17
// Java 17: switch 式で分岐を整理
public String classifyAge(int age) {
    if (age < 0) throw new IllegalArgumentException("負の年齢");
    return switch ((int)(age / 18)) {
        case 0 -> "未成年";
        default -> age < 65 ? "成人" : "高齢者";
    };
}

Library Comparison

JaCoCoJava カバレッジ計測のデファクト。Maven/Gradle プラグインがあり CI 連携が容易。バイトコード計装方式のため、新しい言語機能への対応にタイムラグがある。
Coberturaかつてのデファクト。CI で Cobertura 形式のレポートが求められる場合。開発が停滞。Java 11 以降のサポートが不安定。新規では JaCoCo を選ぶべき。
PITest(ミューテーションテスト)カバレッジだけでは測れないテストの強度を検証したいとき。実行時間が数倍〜数十倍。重要なロジックに絞って使うのが現実的。

注意点

行カバレッジ 100% は分岐カバレッジ 100% を意味しない。if-else の片方だけでも行カバレッジは高く出る

JaCoCo はバイトコード計装方式のため、新しい構文で正確に出ないバージョンがある。0.8.8 以降を推奨

カバレッジ率をノルマにするとアサーションなしの意味のないテストが量産されるリスクがある

Lombok 生成コードもカバレッジ対象になる。除外設定を入れないと数値が下がる

マルチモジュールでは jacoco:report-aggregate で統合レポートを生成する

例外系や境界値のテストが薄いまま数値だけ高く見えることがある。レポートは未通過箇所の発見に使い、品質判断は別軸で行う

カバレッジ閾値を厳しくしすぎると、変更のたびに無理なテスト追加が発生する。チームの開発速度と負債状況に合わせて設定する

FAQ

カバレッジの目標値は何パーセントが適切ですか。

新規コードは 80% 以上を目安にするチームが多いです。数値を絶対視せず、重要なロジックの分岐カバレッジを優先的に確認します。

JaCoCo で特定クラスを除外できますか。

Maven プラグインの excludes 設定で除外パターンを指定できます。DTO や自動生成コードを除外するのが一般的です。

カバレッジが高いのにバグが出るのはなぜですか。

アサーション不足、境界値の未テスト、異常系の未テストが主な原因です。カバレッジは通過の有無しか見ません。

分岐カバレッジと行カバレッジのどちらを重視すべきですか。

条件分岐が多い業務ロジックでは分岐カバレッジの方が有用です。ただし、それだけで十分ではないため、重要な分岐に対する具体的なアサーションも合わせて確認します。

カバレッジゲートは全体値と差分値のどちらで見るべきですか。

既存負債が大きいプロジェクトでは、まず差分カバレッジを管理する方が現実的です。全体値は長期的な改善指標として追うと運用しやすくなります。

関連書籍

この記事のテーマをさらに深く学びたい方へ。

※ Amazon アソシエイトリンクを含みます