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

eclemma branch coverage for switch: 7 of 19 missed

我有这个切换系统,我正在使用 eclemma 来测试分支覆盖率。我们要求所有内容的分支覆盖率至少达到 80%,因此我正在尝试尽可能多地进行测试。 然而,eclemma 告诉我这个开关系统在分支覆盖方面没有经过全面测试。

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 个单独的案例+所有未定义的)。

我尝试搜索有关堆栈溢出的类似问题。其中一些作为解决方案使用 if/else 进行全面覆盖。我不确定这是否是获得此保险的唯一途径。

任何人都可以解释这 19 个分支的来源以及我如何测试剩下的 7 个分支以获得此 switch 案例的 100% 分支覆盖率吗?

查看以下内容Link: http://sourceforge.net/p/eclemma/discussion/614869/thread/80e770df/

下面是上面的片段 link:

这是一个具有 3 种情况的开关的示例:

This is quite an interesting observation. From looking at the byte code one can see how the Java compiler handles the switch on strings. Actually it is an 3-step process:

  1. Switch on the hash code (3 Branches, 1 Default)
  2. For each hash code do an equals (3 * 2 branches)
  3. Do an final switch for the actual execution of the cases (3 Branches, 1 Default)

So we have an total of 14 branches which looks strange from the source code's point of view. What looks even more strange is that you're missing three of them. The explanation is step 2 where the equals method is applied additionally after the hash code. To cover these branches also you would need to find other strings with the same hash code. This is definitely something that might be filtered from coverage reports in future versions of JaCoCo:
https://sourceforge.net/apps/trac/eclemma/wiki/FilteringOptions

Java 编译器将 switch-case 代码转换为 tableswitchlookupswitchtableswitch 当不同情况之间只有少数间隙时使用。否则,使用 lookupswitch

在您的案例中使用了 tableswitch 因为您的案例的哈希码间隔很近(与 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值。)

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

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

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

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

  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 计算两个分支:输入字符串和大小写字符串相等或不相等。因此,我们有 6 * 2 个分支用于比较。(默认情况下不分支。)

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

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

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

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


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

估计以后EclEmma的分支计数会有所调整

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