为什么 JaCoCo 不覆盖我的字符串开关语句?

2022-09-01 23:16:45

我有一个语句,从a中提取寻址模式,我已经编写了单元测试来涵盖,我认为这是每种可能性,但JaCoCo似乎跳过了我的语句,导致覆盖率降低。switchStringswitch

为什么,如果我的所有语句(包括默认值)都在测试中执行,则该语句不计入命中?caseswitch

enter image description here


答案 1

对于字符串的开关

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 编译器生成的字节码类似于以下代码(Eclipse Compiler for 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 种情况的原始 switch 语句在字节码中由一个开关表示,其中 6 个 case 表示为加 5 个 if 语句加上另一个具有 6 个情况的开关。要查看此字节码,您可以使用 。hashCodeStringjavap -c

JaCoCo 执行字节码分析,在低于 0.8.0 的版本中,没有按字符串切换的筛选器。您的测试涵盖 if 语句中的条件计算结果为 的情形,但不包括计算结果为 的情形。就我个人而言,我建议简单地忽略缺少的情况,因为目标不是测试编译器是否生成正确的代码,而是测试应用程序的行为是否正确。但为了这个答案的完整性 - 这是涵盖所有字节码分支的测试:truefalse

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


答案 2