【问题标题】:eclemma branch coverage for switch: 7 of 19 missed切换的 eclemma 分支覆盖率:19 个中的 7 个未命中
【发布时间】:2015-01-18 19:14:24
【问题描述】:

我有这个开关系统,我正在使用 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% 的分支覆盖率?

【问题讨论】:

  • pos 变量 是否有可能获得其他字母值?如果是,添加默认情况。
  • 我添加了第一行代码,说明如何定义 pos。正如我所料,添加一个空的默认情况并没有改变任何东西
  • 我建议不要测试其他分支。一些构造(另见 Java 7s try with resource)生成的字节码比源代码复杂得多。但是,您可以 100% (ok 99,99%) 确保 JRE 在这方面不会出错。因此,请将您的测试工作重点放在其他地方。

标签: java testing switch-statement code-coverage eclemma


【解决方案1】:

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

接下来,如果两个字符串相等,则将存储大小写的索引(字节代码行105-106 用于大小写“G”)。然后跳转到第二个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的分支计数以后会调整。

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

【讨论】:

  • 感谢您的精彩解释。告诉您为什么检测字节码是查看 source 代码测试覆盖率的不好方法;编译器必须在从抽象(源代码)到实现(字节码)的精炼中“添加细节”。添加的细节不是原始应用程序意图的一部分。
  • 这个 SO 答案是不同检测风格的有用背景:stackoverflow.com/questions/15255798/what-are-the-differences-between-the-three-methods-of-code-coverage-analysis有一些测试覆盖率工具只使用源代码计算覆盖率;他们不会有这个问题。这里有几个:(我们的)Java 测试覆盖工具(semanticdesigns.com/Products/TestCoverage)、clover(atlassian.com/software/clover/overview)和 glenmccl.com/instr/index.htm
  • 感谢您花时间解释这一点。虽然它不能解决我的问题,但我现在更了解为什么以及如何解决这个问题。我会尝试修改我的方法,以便获得 80%。
  • @IraBaxter 这一定是 JaCoCo(EclEmma 使用的覆盖引擎)中的一个错误。我自己的代码覆盖工具 (JMockit),它也进行字节码检测,正确识别问题中代码的七个路径;所以,这根本不是 bytecode 检测的问题。就个人而言,我认为源代码代码检测有严重的缺点,不是要走的路。
  • 经过更多测试,前几天我在原始测试中发现了一个错误:我将代码粘贴到原始 Java 6 模块中,因此我将 switch-on-a-String 更改为一个普通的开关。在这种情况下,JMockit Coverage 确实计算了 7 个路径,但在具有原始 switch-on-String 的 Java 7 模块上则不然。发生这种情况的原因是这个答案中解释的(额外的“ifeq”指令和最终的“tableswitch”),但仅仅是因为该工具(就像 JaCoCo)(还)没有明确支持字符串开关。希望我会在下一个版本中添加它。
【解决方案2】:

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

下面是上面链接的sn-p:

这是一个有 3 个开关的例子:

这是一个非常有趣的观察。从看字节 代码一可以看到 Java 编译器如何处理对字符串的切换。 实际上这是一个 3 步过程:

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

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

【讨论】:

  • 我在搜索问题时看到了这一点,但我有点不清楚如何才能覆盖这些分支。如果编译器确实进行了 4 个等于测试,并且我需要涵盖所有内容,那么我需要进行等于 2 个以上案例的测试,这是真的吗?他们正在谈论字符串的哈希码。我怎样才能使用它们以一种相当简单的方式在开关盒中解决这种情况?
  • @Gaargod:您必须为每个 switch 语句计算,为编译器添加了哪些额外的字节码条目。我怀疑您是否真的想这样做,或者您可以以任何实际(例如,适度的时间投资)方式做到这一点。您希望该工具报告源代码中的内容的覆盖率。它没有。不是一个快乐的状态。
  • github.com/jacoco/jacoco/pull/4960.7.10中应该有一个过滤选项
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-02
  • 2016-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多