この記事の目的
JaCoCoでカバレッジがどこまで確認できるか?について機会があったので調べました。
すぐ忘れてしまいそうなので個人的なメモとして記します。
結論
JaCoCoは条件網羅を見るには十分実用的であるが、複合条件網羅までは見れない。
JaCoCoのCoverage定義
https://www.jacoco.org/jacoco/trunk/doc/counters.html
を見ると、明確にサポートしているのは、
C0:命令網羅 (statement coverage, instructions coverage)
C1:分岐網羅 (branch coverage)
であると読み取れます。
そもそもC0~C2の定義は?
経験則としてC2については各プロジェクトや現場で微妙に解釈が揺れているケースが多いと思います。
ここでは、
ホワイトボックステストにおけるカバレッジ(C0/C1/C2/MCC)について
に沿って定義しておきます。
- C0 命令網羅 (statement coverage)
- C1 分岐網羅 (branch coverage)
- C2 条件網羅 (condition coverage)
- MCC 複合条件網羅 (multiple condition coverage)
先ほど書いたようにC2とMCCが混同されているケースが多いように思います。
サンプルコード
Target Code (Testee)
package com.example.util;
public class Coverage {
public static void or(int x , int y) {
if (x == 0 || y <= 10) {
System.out.println("true");
} else {
System.out.println("false");
}
}
public static void and(int x , int y) {
if (x == 0 && y <= 10) {
System.out.println("true");
} else {
System.out.println("false");
}
}
public static void caseStatement(int x) {
switch (x) {
case 0:
System.out.println("0");
break;
case 1:
case 2:
case 3:
case 4:
System.out.println("1~4");
default:
System.out.println("default");
}
}
public static void ifStatement(int x) {
if (x == 0) {
System.out.println("0");
} else if (x == 1 || x == 2 || x == 3 || x == 4) {
System.out.println("1~4");
} else {
System.out.println("default");
}
}
}
Test Code (tester)
package com.example.util;
import org.junit.jupiter.api.Test;
public class CoverageTest {
@Test
public void testCoverage() {
var coverage = new Coverage();
}
@Test
public void testOrTrueTrue() {
// True || True -> True
Coverage.or(0, 10);
}
@Test
public void TestOrTrueFalse() {
// True || False -> True
Coverage.or(0, 11);
}
@Test
public void TestOrFalseTrue() {
// False || True -> True
Coverage.or(1, 10);
}
@Test
public void TestOrFalseFalse() {
// False || False -> False
Coverage.or(1, 11);
}
// And tests
@Test
public void testAndTrueTrue() {
// True && True -> True
Coverage.and(0, 10);
}
@Test
public void TestAndTrueFalse() {
// True && False -> False
Coverage.and(0, 11);
}
@Test
public void TestAndFalseTrue() {
// False && True -> False
Coverage.and(1, 10);
}
@Test
public void TestAndFalseFalse() {
// False && False -> False
Coverage.and(1, 11);
}
// caseStatement tests
@Test
public void testCaseStatement0() {
// 0 -> 0
Coverage.caseStatement(0);
}
@Test
public void testCaseStatement1() {
// 1 -> 1~4
Coverage.caseStatement(1);
}
@Test
public void testCaseStatement2() {
// 2 -> 1~4
Coverage.caseStatement(2);
}
@Test
public void testCaseStatement3() {
// 3 -> 1~4
Coverage.caseStatement(3);
}
@Test
public void testCaseStatement4() {
// 4 -> 1~4
Coverage.caseStatement(4);
}
@Test
public void testCaseStatement5() {
// 5 -> default
Coverage.caseStatement(5);
}
// ifStatement tests
@Test
public void testIfStatement0() {
// 0 -> 0
Coverage.ifStatement(0);
}
@Test
public void testIfStatement1() {
// 1 -> 1~4
Coverage.ifStatement(1);
}
@Test
public void testIfStatement2() {
// 2 -> 1~4
Coverage.ifStatement(2);
}
@Test
public void testIfStatement3() {
// 3 -> 1~4
Coverage.ifStatement(3);
}
@Test
public void testIfStatement4() {
// 4 -> 1~4
Coverage.ifStatement(4);
}
@Test
public void testIfStatement5() {
// 5 -> default
Coverage.ifStatement(5);
}
}
図説
ここでは、orメソッドのみ説明します。
public static void or(int x , int y) {
if (x == 0 || y <= 10) {
System.out.println("true");
} else {
System.out.println("false");
}
}
C2(条件網羅)を満たすためには、変数xとyの判定がそれぞれ少なくとも1度はTrueおよびFalseになるようなテストケースを用意すれば十分です。つまり、以下の2つのテストケースでOKとなります。
x==0 : True, y<=10 : False
x==0 : False, y<=10 : True
ただし、この場合、else文に入るケースが存在しないため、C1の条件は満たされません。
実際にC1とC2の両方を満たす最低限のテストケースは、以下の3つです。
x==0 : True, y<=10 : False
x==0 : False, y<=10 : True
x==0 : False, y<=10 : False
この状態ですと、JaCoCoは表示上All Greenになります。
MCCまで満たす場合
MCC(複合条件網羅)も満たす場合には、すべての条件の組み合わせをテストする必要があります。したがって、以下の4つのテストケースが必要となります。
- x==0 : True, y<=10 : True
- x==0 : True, y<=10 : False
- x==0 : False, y<=10 : True
- x==0 : False, y<=10 : False
この場合にも、JaCoCoは表示上All Greenですが、1つ目と2つ目のテストケースが存在しない場合との区別は付きません。
構造ベースのテストでは、このor条件は1つ目と2つ目のケースが等価であると言えます(x == 0がTrueであれば次の条件を判定しないため)。しかし、仕様ベースのテストでは、MCCを満たさないと不十分という考え方もあります。
まとめ
結論として、JaCoCoを使用して構造ベースのテストの網羅性を確認し、仕様ベースのテスト網羅性は別途担保するというアプローチが現実的であると考えられます。