【问题标题】:Eclemma says 1 of 4 branches not covered, but which branch is it?Eclemma 说 4 个分支中的 1 个没有被覆盖,但它是哪个分支?
【发布时间】:2013-03-19 06:43:43
【问题描述】:

有没有一种简单的方法来判断我缺少哪个分支? IE。我有一些这样的代码:

if (x || y) {
    // do stuff
}

在突出显示的报道中,Eclipse 中有一个黄点,上面写着:

错过了 4 个分支中的 1 个

但我想知道缺少哪个分支。

【问题讨论】:

    标签: java eclipse code-coverage eclemma


    【解决方案1】:

    xy 可以是什么?

    • true || truetrue(因 JVM 优化未涵盖:如果第一个条件为 true,则第二个条件因短路评估而不会被评估)
    • false || true真实
    • true || false真实
    • false || false

    【讨论】:

    • 问题是,我怎么知道哪些分支没有被遵循?我确定这不是假/假情况,因为在某些情况下会执行“其他”情况。
    • 不应该是真的吗||没有涵盖的真实案例?
    • 我认为这个问题的重点是有时你有复杂的条件,比如 foo != null && foo.equals("bar")。或者我曾遇到过必须对难以跟踪的荒谬遗留控制语句进行单元测试的情况。
    • “由于 JVM 优化而未涵盖”语句不太正确。这不是优化:在|| 运算符的语言规范中,如果第一个操作数的计算结果为真,则不能计算第二个操作数。这是一个硬性功能要求,而不是优化,并且是用 Java 语言而不是 JVM 定义的。
    • 而且...实际上,当第一个操作数是true 时它不能评估第二个操作数,它如何区分true || truetrue || false?我看不出y=truey=false 是如何区分的!我认为只有 3 种情况:truefalse||truefalse||false - 你怎么看?这闻起来像是覆盖工具中的错误......!
    【解决方案2】:

    有一个非常简单的解决方法 - 只需将每个逻辑谓词放在单独的行上,如下所示:

    if (x 
        || y) {
        System.out.println("BRANCH: " + x + ", " + y);
        // Do stuff
    }
    

    现在,当您运行分析时,标记应直接指向遗漏的分支。添加覆盖率后,您可以以正确的方式重新格式化您的代码。

    【讨论】:

    • 太棒了!顺便说一句,如果 Eclipse 的“保存时格式化”一直把这些放回一行,只需在文本编辑器中编辑文件即可。
    【解决方案3】:

    在 Eclemma 的父母 jacoco 的 github 存储库上的 open issue 表明,这样的功能实际上有点难以包含。

    但是,即使没有 Eclemma 功能,如果目标只是找出在特定情况下遗漏的分支,您也可以检测代码以进行跟踪。最简单的例子是老式的打印语句:

    if (x || y) {
        System.out.println("BRANCH: " + x + ", " + y);
        // Do stuff
    }
    

    然后查看输出,看看你实际点击了哪些分支(例如java ... | grep "BRANCH:" | sort | uniq)。 (不是很满意,我知道。)

    【讨论】:

    • 我在像return expr1 && expr2这样的行上有同样的错误;我有单元测试,执行返回值为真。所以它一定已经访问了这两个表达式。 ://
    【解决方案4】:

    答案是true|| true 未被覆盖。

    这是因为一旦 JVM 发现第一个条件为真,它就不会运行第二个条件(已优化),这意味着这部分代码永远不会运行。

    正如 Maroun 所说,4 个分支中有 3 个将允许条件通过。如果您仍然担心代码覆盖率,可以将条件重构为 && 而不是 ||

    (x || y)(!(!x && !y)) 相同,这将允许您测试所有条件,因为现在只有三个分支。

    条件的原始形式经常出现在保护语句中:

    if (obj == null || obj.hasError())
    {
        throw new RuntimeException();
    }
    

    这将永远不允许您检查 obj 是否为 null 并且有错误,因为它会抛出空指针异常。

    如果代码覆盖率很重要,那么只需使用这种形式:

    if (!(obj != null && !obj.hasError()))
    {
        throw new RuntimeException();
    }
    

    【讨论】:

    • 或者也许可以使用 Eager 运算符 |& 除非您故意将操作短路,在这种情况下您永远无法获得完整的分支覆盖。
    • @BrettPyke 在 '&&' 的情况下怎么会有三个分支?
    • @BrettPyke 如果您将 (x || y) 转换为 !(!x && !y),您将遇到与表达式 !(false && false) 相同的收敛快捷问题。
    • @LeonidGlanz 没错。如果覆盖工具中存在错误,它错误地评估了 OR 条件而不是 AND,那么这是一回事。但是做一个逻辑反转并没有改变这样一个事实,即有 4 种情况,其中只有 3 种是可达的。
    【解决方案5】:

    可能存在来自if 块内的嵌套语句或扩展xy 谓词的语句的隐式分支。

    阅读: http://emma.sourceforge.net/faq.html#q.fractional.examples

    【讨论】:

    • 是的,我想。 x 和 y 布尔值有 4 种不同的可能组合,我猜只有其中三个被观察到。问题是,我怎么知道哪个案例没有发生?或者等效地,观察了哪些案例?
    • “1 of 4 个分支已覆盖”表示未覆盖 3 个分支。我认为应该很明显(从块内的覆盖突出显示)哪些代码路径没有被覆盖。
    • 对不起,完全是我的错!该消息实际上说“错过了 4 个分支中的 1 个”。我马上更新……
    【解决方案6】:

    下面的表达式会是什么结果?

    • true || truetrue
    • true || false真实
    • false || true真实
    • false || false

    如果您注意到第一种和第二种情况,当您将第一个参数设置为 TRUE 时,则无需检查第二个参数。

    • true && truetrue
    • true && false错误
    • false && true
    • false && false

    或者,如果您看到带有 AND 运算符的场景,如果第一个参数为 FALSE,则您永远不需要检查第二个参数。

    因此您永远无法检查 4 个分支中的 1 个分支。

    【讨论】:

    • 那么 - 这是否意味着覆盖工具警告不要测试完全无法访问的分支?这听起来像一个错误。
    【解决方案7】:

    要获得全面覆盖,避免编译器的隐式选择,您必须将 if 语句重写为两个 as

    if (x) {
        // do stuff
    } else if (y) {
        // do the same stuff
    }
    

    这将得到全面覆盖,但会被正确标记为冗余代码。看到问题后,我宁愿忍受黄线,也不愿将这种混乱的结构添加到代码中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-22
      • 1970-01-01
      • 2011-06-05
      • 2019-06-15
      相关资源
      最近更新 更多