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

Java 为什么JaCoCo没有涵盖我的字符串开关语句?,java,junit,switch-statement,jacoco,codecov,Java,Junit,Switch Statement,Jacoco,Codecov,我有一个switch语句,它从String中提取一个寻址模式,我编写了单元测试来涵盖,我认为这是每一种可能发生的情况,但JaCoCo似乎跳过了我的switch语句,导致覆盖率较低 为什么,如果我的所有case语句(包括一个默认值)都在测试中执行,那么switch语句是否不被视为命中 用于按字符串切换 class Fun { static int fun(String s) { switch (s) { case "I": return 1;

我有一个
switch
语句,它从
String
中提取一个寻址模式,我编写了单元测试来涵盖,我认为这是每一种可能发生的情况,但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编译器生成的字节码与以下代码类似(Eclipse编译器for Java生成的字节码略有不同)

因此,具有6个大小写的原始switch语句在字节码中表示为一个具有6个大小写的开关,用于
hashCode
of
String
加上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生成的报告作为证据:

,包括
javac
为按字符串切换生成的字节码过滤器。因此,即使没有其他测试,也会生成以下报告:


此问题可能与以下问题有关:。另外一个选择是,如果
t[OP\u ADD]
为空,则不测试NPE。相关问题:我不是在测试
NullPointerException
但是,我在测试
ArrayOutOfBoundsIndexException
,因为有些指令有寻址模式,例如
OP\u LDA\u I
,而有些指令是立即寻址的,例如
OP\u SEC
技术上,
t[OP\u ADD]
,在这种情况下,将发生NullPointerException。在我看来,为了涵盖这种情况,您需要添加空值测试。哦,您是说我应该测试空值。但是这会出现在
if
中,那么为什么这会影响我的
切换
覆盖范围呢?因此,查找与我的所有case语句
String
具有相同hascode的
字符串
或者咧嘴笑并裸露它( Thanks@RossDrew就我个人而言,我建议忽略遗漏的案例我接受这个答案,直到我编写测试案例来涵盖它。我添加了测试,检查我的每个
开关
案例与
hashcode()
相同但
等于()的案例
是不同的。应该检查嵌套的
if
但代码覆盖率保持不变。@添加调试输出
if(t[OP\u ADD].startsWith(“\0”))System.out.println(“已测试”)
就在开关告诉我您实际上没有测试相同哈希代码的案例之前。您也可以在调试器的帮助下确认这一点。在您的测试中,
\0
应该放在
I
A
Z
ABS
IND
之前,而不是在
OP
之前,即
OP\u或
之前A\u0abs
等@BaptistePernet见JSR-334()"为了使开关中的字符串不受语言变化的影响,Java SE 7中的JVM lookupswitch和tableswitch指令不支持开关字符串。相反,Java编译器负责将开关字符串转换为具有正确语义的字节码指令序列。许多有效且高效的转换EME可能具有比连续比较每个案例标签常量打开的字符串更好的预期性能。”
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(""));
  }
}