Why is JaCoCo not covering my String switch statements?(为什么 JaCoCo 没有涵盖我的 String switch 语句?)
问题描述
我有一个从 String
中提取寻址模式的 switch
语句,并且我已经编写了单元测试来涵盖,我认为这是所有可能发生的事情,但 JaCoCo 似乎跳过我的 switch
语句,导致覆盖率较低.
为什么,如果我的所有 case
语句(包括默认值)都在测试中执行,那么 switch
语句不会算作命中吗?
对于字符串切换
类乐趣{静态 int 乐趣(字符串 s){开关 (s) {案例我":返回 1;案例A":返回 2;案例Z":返回 3;案例ABS":返回 4;案例IND":返回 5;默认:返回 6;}}}
Oracle Java 编译器生成的字节码类似于以下代码(Eclipse Compiler for Java 生成的字节码略有不同)
int c = -1;开关(s.hashCode()){案例 65://+1 分支if (s.equals("I"))//+2 个分支c = 0;休息;案例 73://+1 分支if (s.equals("A"))//+2 个分支c = 1;休息;案例 90://+1 分支if (s.equals("Z"))//+2 个分支c = 2;休息;案例 64594://+1 分支if (s.equals("ABS"))//+2 个分支c = 3;休息;案例 72639://+1 分支if (s.equals("IND"))//+2 个分支c = 4;休息;默认值://+1 分支}开关 (c) {case 0://+1 分支返回 1;案例 1://+1 分支返回 2;案例 2://+1 分支返回 3;案例 3://+1 分支返回 4;案例 4://+1 分支返回 5;默认值://+1 分支返回 6;}
因此,具有 6 个 case 的原始 switch 语句在字节码中由 String
的 hashCode
的 6 个 case 加上 5 个 if 语句加上另一个具有 6 个 case 的 switch 表示案例.要查看此字节码,您可以使用 javap -c
.
JaCoCo 执行字节码分析,并且在低于 0.8.0 的版本中没有按字符串切换的过滤器.您的测试涵盖 if 语句中的条件评估为 true
的情况,但不包括它们评估为 false
的情况.就我个人而言,我建议简单地忽略丢失的情况,因为目标不是测试编译器生成正确的代码,而是测试您的应用程序是否正确运行.但是为了这个答案的完整性 - 这里是涵盖所有字节码分支的测试:
import org.junit.Test;导入静态 org.junit.Assert.*;公共类 FunTest {@测试公共无效测试(){//原始字符串: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"));//相同的哈希码,但不同的字符串:assertEquals(6, Fun.fun(" I"));assertEquals(6, Fun.fun(" A"));assertEquals(6, Fun.fun(" Z"));assertEquals(6, Fun.fun(" ABS"));assertEquals(6, Fun.fun(" IND"));//不同的哈希码来覆盖开关的默认情况assertEquals(6, Fun.fun(""));}}
JaCoCo 0.7.9 生成的报告作为证明:
I have a switch
statement that extracts an addressing mode from a String
and I've written unit tests to cover, what I thought was every eventuality but JaCoCo seems to skip my switch
statements, resulting in lower coverage.
Why, if all my case
statements, including a default are being executed in tests, would the switch
statement not be counted as hit?
For the switch by String
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 compiler generates bytecode similar to the following code (Eclipse Compiler for Java generates slightly different bytecode)
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;
}
So that original switch-statement with 6 cases is represented in bytecode by a switch with 6 cases for hashCode
of String
plus 5 if-statements plus another switch with 6 cases. To see this bytecode you can use javap -c
.
JaCoCo performs analysis of bytecode and in versions lower than 0.8.0 has no filter for switch by string. Your tests cover cases, where conditions in if-statements evaluate to true
, but not the cases where they evaluate to false
. Personally I would advise to simply ignore missing cases, because the goal is not to test that compiler generates proper code, but to test that your application behaves correctly. But for a sake of completeness of this answer - here is tests that cover all bytecode branches:
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(" I"));
assertEquals(6, Fun.fun(" A"));
assertEquals(6, Fun.fun(" Z"));
assertEquals(6, Fun.fun(" ABS"));
assertEquals(6, Fun.fun(" IND"));
// distinct hash code to cover default cases of switches
assertEquals(6, Fun.fun(""));
}
}
And report generated by JaCoCo 0.7.9 as a proof:
JaCoCo version 0.8.0 provides filters, including filter for bytecode that javac
produces for switch by string. And so generates following report even without additional tests:
这篇关于为什么 JaCoCo 没有涵盖我的 String switch 语句?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!