为什么 JaCoCo 没有覆盖我的 String switch 语句?

Why is JaCoCo not covering my String switch statements?

我有一个从 String 中提取寻址模式的 switch 语句,我已经编写了单元测试来涵盖,我认为是所有可能发生的事情,但 JaCoCo 似乎跳过了我的 switch 语句,导致覆盖率较低。

为什么,如果我的所有 case 语句(包括默认语句)都在测试中执行,那么 switch 语句不会算作命中吗?

对于字符串的切换

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 的 Eclipse 编译器生成的字节码略有不同)

    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 个 case 的 switch 语句在字节码中表示为 hashCode of String 的 6 个 case 的 switch 加上 5 个 if 语句加上另一个 6 case 的 switch。要查看此字节码,您可以使用 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("[=12=]I"));
    assertEquals(6, Fun.fun("[=12=]A"));
    assertEquals(6, Fun.fun("[=12=]Z"));
    assertEquals(6, Fun.fun("[=12=]ABS"));
    assertEquals(6, Fun.fun("[=12=]IND"));

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

并以JaCoCo 0.7.9生成的报告为证:

JaCoCo version 0.8.0 provides filters,包括 javac 为按字符串切换而生成的字节码过滤器。因此即使没有额外的测试也会生成以下报告: