java Switch 的 eclemma 分支覆盖范围:19 个中的 7 个丢失

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28013717/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 12:49:43  来源:igfitidea点击:

eclemma branch coverage for switch: 7 of 19 missed

javatestingswitch-statementcode-coverageeclemma

提问by Gaargod

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.

我有这个开关系统,我正在使用 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;
        }

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).

我使用了简单的 JUnit 测试来测试这些案例中的每一个。仍然 eclemma 将此标记为黄色并表示“19 个分支中有 7 个丢失”。我想说只有 7 种方法可以通过这个切换系统(6 个单独的案例 + 全部未定义)。

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.

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

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?

任何人都可以解释所有这 19 个分支的来源,以及我如何测试剩余的 7 个分支以获得 100% 的分支覆盖率?

回答by nrainer

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

Java 编译器将 switch-case 代码转换为 atableswitch或 a lookupswitch。在tableswitch当只有少数差距是,不同的情况之间的被使用。否则,将lookupswitch使用 。

In your case a tableswitchis usedbecause the hash codes of your cases are closely spaced (unlike in the code referenced by owaism):

在您的情况下tableswitch,使用a是因为您的情况的哈希码间隔很近(与 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
      }

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.)

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

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

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

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.

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

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.

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

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

案例“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 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.)

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

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

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

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

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).

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

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

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



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...

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

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

未来可能会调整 EclEmma 的分支计数。

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.)

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

回答by owaism

Check out the following Link: http://sourceforge.net/p/eclemma/discussion/614869/thread/80e770df/

查看以下链接:http: //sourceforge.net/p/eclemma/discussion/614869/thread/80e770df/

Below is snippet from the above link:

以下是上述链接的片段:

This for an example having a switch with 3 cases:

这是具有 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 编译器如何处理字符串的切换。实际上,它是一个 3 步过程:

  1. 打开哈希码(3 个分支,1 个默认值)
  2. 对于每个哈希码做一个等于(3 * 2 个分支)
  3. 为实际执行案例做最后的切换(3 个分支,1 个默认)

所以我们总共有 14 个分支,从源代码的角度来看,这看起来很奇怪。看起来更奇怪的是你错过了其中的三个。解释是第 2 步,其中在哈希码之后额外应用了 equals 方法。为了覆盖这些分支,您还需要找到具有相同哈希码的其他字符串。这绝对是可能会从 JaCoCo 未来版本的覆盖率报告中过滤掉的内容:https://sourceforge.net/apps/trac/eclemma/wiki/FilteringOptions