23

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

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

在此处输入图像描述

4

1 回答 1

30

通过字符串切换

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 个 case 的原始 switch 语句在字节码中由一个带有 6 个 case 的 switchhashCode加上String5 个 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("\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 生成的报告作为证明:

覆盖率报告

JaCoCo 0.8.0 版提供了过滤器,包括javac为通过字符串切换生成的字节码过滤器。因此即使没有额外的测试也会生成以下报告:

覆盖率报告

于 2017-03-08T19:32:28.013 回答