web-dev-qa-db-ja.com

JaCoCoがString switchステートメントをカバーしないのはなぜですか?

switchからアドレッシングモードを抽出するStringステートメントがあり、カバーするユニットテストを作成しました。私はすべての不測の事態であると考えていましたが、JaCoCoはswitchステートメントをスキップし、カバレッジが低くなります。

デフォルトを含むすべてのcaseステートメントがテストで実行されている場合、switchステートメントはヒットとしてカウントされないのはなぜですか?

CodeCov に表示されるテスト結果を参照)

enter image description here

18
Ross Drew

文字列による切り替えの場合

class Fun  {
  static int fun(String s) {
    switch (s) {
      case "I":
        return 1;
      case "A":
        return 2;
      case "Z":
        return 3;
      case "ABS":
        return 4;
      case "IND":
        return 5;
      default:
        return 6;
    }
  }
}

Oracle Javaコンパイラーは、次のコードに類似したバイトコードを生成します(Javaは、わずかに異なるバイトコードを生成します)

    int c = -1;
    switch (s.hashCode()) {
      case 65: // +1 branch
        if (s.equals("I")) // +2 branches
          c = 0;
        break;
      case 73: // +1 branch
        if (s.equals("A")) // +2 branches
          c = 1;
        break;
      case 90: // +1 branch
        if (s.equals("Z")) // +2 branches
          c = 2;
        break;
      case 64594: // +1 branch
        if (s.equals("ABS")) // +2 branches
          c = 3;
        break;
      case 72639: // +1 branch
        if (s.equals("IND")) // +2 branches
          c = 4;
        break;
      default: // +1 branch
    }
    switch (c) {
      case 0: // +1 branch
        return 1;
      case 1: // +1 branch
        return 2;
      case 2: // +1 branch
        return 3;
      case 3: // +1 branch
        return 4;
      case 4: // +1 branch
        return 5;
      default: // +1 branch
        return 6;
    }

したがって、6ケースの元のスイッチ文は、hashCode of Stringに6ケースのスイッチと、5つのif文と6ケースの別のスイッチのバイトコードで表されます。このバイトコードを表示するには、javap -cを使用できます。

JaCoCoはバイトコードの分析を実行し、0.8.0より前のバージョンでは、文字列による切り替え用のフィルターはありません。テストでは、if文の条件がtrueに評価されるケースをカバーしていますが、falseに評価されるケースはカバーしていません。目標は、コンパイラが適切なコードを生成することをテストすることではなく、アプリケーションが正しく動作することをテストすることです。しかし、この答えを完全にするために、すべてのバイトコードブランチをカバーするテストを次に示します。

import org.junit.Test;
import static org.junit.Assert.*;

public class FunTest {
  @Test
  public void test() {
    // original strings:
    assertEquals(1, Fun.fun("I"));
    assertEquals(2, Fun.fun("A"));
    assertEquals(3, Fun.fun("Z"));
    assertEquals(4, Fun.fun("ABS"));
    assertEquals(5, Fun.fun("IND"));

    // same hash codes, but different strings:
    assertEquals(6, Fun.fun("\0I"));
    assertEquals(6, Fun.fun("\0A"));
    assertEquals(6, Fun.fun("\0Z"));
    assertEquals(6, Fun.fun("\0ABS"));
    assertEquals(6, Fun.fun("\0IND"));

    // distinct hash code to cover default cases of switches
    assertEquals(6, Fun.fun(""));
  }
}

そして、証拠としてJaCoCo 0.7.9によって生成されたレポート:

coverage report

JaCoCoバージョン0.8.0はフィルターを提供しますjavacが文字列によるスイッチ用に生成するバイトコードのフィルターを含みます。したがって、追加のテストがなくても次のレポートが生成されます。

coverage report

24
Godin