有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java为什么JaCoCo没有涵盖我的字符串开关语句?

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

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

enter image description here


共 (1) 个答案

  1. # 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编译器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个大小写的switch语句加上5个if语句加上另一个具有6个大小写的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("\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 version 0.8.0 provides filters,包括javac按字符串为开关生成的字节码的过滤器。因此,即使没有其他测试,也会生成以下报告:

    coverage report