为什么 JaCoCo 不覆盖我的字符串开关语句?
2022-09-01 23:16:45
对于字符串的开关
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 个情况的开关。要查看此字节码,您可以使用 。hashCode
String
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