切换的 eclemma 分支覆盖率:19 个中的 7 个未命中

我有这个开关系统,我正在使用 eclemma 来测试分支覆盖率.我们需要对所有内容至少有 80% 的分支覆盖率,所以我正在尝试尽可能多地进行测试.然而,eclemma 告诉我这个交换机系统在分支覆盖方面没有经过全面测试.

I have this switch system and I'm using eclemma to test the branch coverage. We are required to have at least 80% in branch coverage for everything so I'm trying to test as much as possible. However, eclemma tells me this switch system is not fully tested in terms of branch coverage.

pos = p.getCurrentPosition().substring(0, 1);
switch (pos) {
            case "G":
                goalkeepers++;
                break;
            case "D":
                defense++;
                break;
            case "M":
                midfield++;
                break;
            case "F":
                offense++;
                break;
            case "S":
                substitutes++;
                break;
            case "R":
                reserves++;
                break;
        }

我使用简单的 JUnit 测试来处理每种情况.仍然 eclemma 将此标记为黄色,并表示19 个分支中有 7 个丢失".我想说只有 7 种方法可以通过这个开关系统(6 个单独的案例 + 全部未定义).

I used straightforward JUnit tests to go trough each of these cases. Still eclemma marks this as yellow and says "7 of 19 branches missed". I would say there are only 7 ways to go through this switch system (the 6 individual cases+all undefined).

我尝试搜索有关堆栈溢出的类似问题.其中一些具有使用 if/else 进行全面覆盖的解决方案.我不确定这是否是获得此覆盖范围的唯一方法.

I tried searching for similar questions on stack overflow. Some of them had as solutions to use if/else for full coverage. I'm not sure if this is the only way possible to get this coverage.

谁能解释所有这 19 个分支的来源以及我如何测试剩余的 7 个分支以在此 switch 案例上获得 100% 的分支覆盖率?

Can anybody explain where all these 19 branches come from and how I could test these remaining 7 to get a 100% branch coverage on this switch case?

推荐答案

Java 编译器将 switch-case 代码转换为 tableswitchlookupswitch.tableswitch 用于不同情况之间只有少量间隙的情况.否则,使用 lookupswitch.

The Java compiler translates the switch-case code either to a tableswitch or to a lookupswitch. The tableswitch is used when there are only a few gaps are between the different cases. Otherwise, the lookupswitch is used.

在您的情况下,使用了 tableswitch,因为您的情况的哈希码间隔很近(与 owaism 引用的代码不同):

In your case a tableswitch is used because the hash codes of your cases are closely spaced (unlike in the code referenced by owaism):

  16: tableswitch   { // 68 to 83
                68: 111 // 'D'
                69: 183
                70: 141 // 'F'
                71: 96  // 'G'
                72: 183
                73: 183
                74: 183
                75: 183
                76: 183
                77: 126 // 'M'
                78: 183
                79: 183
                80: 183
                81: 183
                82: 171 // 'R'
                83: 156 // 'S'
           default: 183
      }

冒号左边的数字是有序的哈希码和它们之间的填充间隙,右边的数字是跳转目的地.(在 Java 中,一个字符的哈希码就是它的 ASCII 值.)

The numbers to the left of the colon are the ordered hash codes and the filled gaps between them, the numbers to the right are the jump destinations. (In Java, the hash code of a character is its ASCII value.)

68是D"(最低位)的哈希码,83是S"(最高位)的哈希码.69 是真实案例之间的差距值之一,会跳转到默认案例.

68 is the hash code of "D" (the lowest one), and 83 is the hash code of "S" (the highest one). 69 is the value of one of the gaps between the real cases and will jump to the default case.

但是,我假设 EclEmma 从 tableswitch 的覆盖率计算中排除了这些分支(由于存在差距,它会进一步降低覆盖率).所以我们还有 0 个(计数)分支.

However, I assume that EclEmma excludes these branches from the coverage computation of a tableswitch (it would lower the coverage even more because of the gaps). So we have 0 (counted) branches yet.

接下来,在每个跳转目标处执行字符串值的相等比较(默认情况除外).由于您的 switch-case 由 6 个 case 组成,因此我们有 6 个 6 个跳转目的地并进行相等比较.

Next, an equals comparison of the string value is performed at each jump destination (except at the one of the default case). As your switch-case consists of 6 cases, we have 6 six jump destinations with an equals comparison.

案例G"的比较字节码如下:

The byte code of the comparison for case "G" is below:

  96: aload_3
  97: ldc           #10
  99: invokevirtual #11  java/lang/Object;)Z
 102: ifeq          183
 105: iconst_0
 106: istore        4
 108: goto          183
 111: aload_3

EclEmma 计算两个分支:输入字符串和 case 字符串相等或不相等.因此,我们有 6 * 2 个分支用于比较.(默认情况不分支.)

EclEmma counts two branches: either the input string and the case string are equals or they are not. Thus, we have 6 * 2 branches for the comparisons. (The default case does not branch.)

接下来,如果两个字符串相等,则将存储大小写的索引(字节代码行 105-106 用于大小写G").然后跳转到第二个 tableswitch 将被执行.否则直接跳转.

Next, if the two strings are equal the index of the case will be stored (byte code lines 105-106 for the case "G"). Then a jump to the second tableswitch will be executed. Otherwise, the jump will be executed directly.

 185: tableswitch   { // 0 to 5
                 0: 224
                 1: 237
                 2: 250
                 3: 263
                 4: 276
                 5: 289
           default: 299
      }

这个开关对之前存储的case索引进行操作,跳转到case中的code(caseG"有索引0,默认case有-1).EclEmma 计数 7 个分支(6 个案例加上默认案例).

This switch operates on the previously stored case index and jumps to the code in the case (case "G" has index 0, the default case has -1). EclEmma counts 7 branches (6 cases plus the default case).

因此,我们在第一个 tableswitch 中有 0 个计数分支,在 equals 比较中有 12 个分支,在第二个 tableswitch 中有 7 个分支.总而言之,这会产生 19 个分支.

Consequently, we have 0 counted branches in the first tableswitch, 12 branches in the equals comparisons and further 7 branches in the second tableswitch. All in all, this results in 19 branches.

您的测试未涵盖 6 个不等于分支中的任何一个.为了涵盖这些,您需要为每个案例找到一个不等于案例条件但具有相同哈希码的字符串.这是可能的,但绝对不明智......

Your tests do not cover any of the 6 not-equals branches. In order to cover these, you would need to find a string for each case which is not equal to the case condition but has the same hash code. It is possible, but definitively not sensible...

可能EclEmma的分支计数以后会调整.

Probably, the branch counting of EclEmma will be adjusted in the future.

此外,我猜您没有与任何案例都不匹配的测试案例(因此不包括 (隐式)默认案例.)

Moreover, I guess you don't have a test case which does not match with any of the cases (thus the (implicit) default case is not covered.)

相关文章