Enumのテストでカバレッジが100%にならない謎


Enumの謎

列挙型のクラスに対する JUnit テストを行った際、ソースコードを全網羅しているにも関わらず、カバレッジが 100% にならないことがあります。なぜでしょう?

Answer.java(テスト対象の列挙型)
public enum Answer {
    YES,
    NO;

    public Answer opposite() {
        return this == YES ? NO : YES;
    }
}
AnswerTest.java(テストコード)
public class AnswerTest {

    @Test
    public void testOpposite() {
        assertThat(Answer.YES.opposite(), theInstance(Answer.NO));
        assertThat(Answer.NO.opposite(), theInstance(Answer.YES));
    }
}

答え

Answer#values() と Answer#valueOf(String) に対するテストコードが無いためでした。これらを追加することで、カバレッジが 100% になります。

AnswerTest.java’(テストコード改)
public class AnswerTest {

    @Test
    public void testOpposite() {
        assertThat(Answer.YES.opposite(), theInstance(Answer.NO));
        assertThat(Answer.NO.opposite(), theInstance(Answer.YES));
    }

    // 追加
    @Test
    public void testValues() {
        assertThat(Answer.values(), is(new Answer[] { Answer.YES, Answer.NO }));
    }

    // 追加    
    @Test
    public void testValueOf() {
        assertThat(Answer.valueOf("YES"), theInstance(Answer.YES));
        assertThat(Answer.valueOf("NO"), theInstance(Answer.NO));
    }
}

解説

列挙型には次の2つのメソッドが暗黙的に定義されており、コンパイラにより自動で生成されます。

  • public static E[] values()
  • public static E valueOf(String name)

多くのカバレッジレポートは、ソースコードではなくバイトコードに対するカバレッジを測定するものですので、このような振る舞いになったのですね。

四方山話

ある程度の大きさのプロジェクトになると、「単体テストでは C1 カバレッジ 100% を目標とする!」「100% に満たない場合はその妥当性をレビュアが確認する!!」なんていうテスト計画が立てられちゃったりします。

values() と valueOf(String) は自動生成されるものなのでテストする必要なんてもちろん無いわけですが、「カバレッジ 100% になってないけどどうなってるの?!」なんて聞いてくる "品質保証担当者" に説明するのとテストしちゃうのと、どっちが楽かという話ですね...

 ・・・

本稿は、以前の投稿 のソースコードからのスピンオフ記事になります。Color クラスなどのテストコードを書いていて気づいたので投稿しました。
よろしければこちらの投稿も見てやってちょ → 「リバーシで遊んで覚える Java 8.」

なお nmby の流儀は、test first ならぬ test later です。。。

 ・・・

本稿の内容は初歩的なものでした。
Qiita の投稿に難易度フラグ(初歩~高度)なんかがあると、便利かもしれませんね。