【问题标题】:How far do you take code coverage? [closed]你的代码覆盖率有多远? [关闭]
【发布时间】:2008-10-31 14:01:10
【问题描述】:

我最近开始使用代码覆盖工具(尤其是 Emma 和 EclEmma),我非常喜欢它为我提供的关于单元测试完整性的观点 - 以及查看单元代码的哪些区域的能力测试根本没有命中。我目前在一个不做大量单元测试的组织工作,我计划真正推动每个人进行单元测试和代码覆盖率和 TDD,并希望改变组织。

对于这个主题,我不确定的一个问题是我应该将代码覆盖率究竟走多远。例如,如果我有这样的类:

//this class is meant as a pseudo-enum - I'm stuck on Java 1.4 for time being
public final class BillingUnit {

    public final static BillingUnit MONTH = new BillingUnit("month");
    public final static BillingUnit YEAR = new BillingUnit("year");

    private String value;

    private BillingUnit(String value) {
        this.value = value;
    }

    public String getValue() {
        return this.value;
    }

    public boolean equals(Object obj) {
        return value.equals(((BillingUnit) obj).getValue());

    }

    public int hashCode() {
        return value.hashCode();
    }
}

我编写了一些简单的单元测试以确保 equals() 正常工作,getValue() 返回我所期望的,等等。但是由于 EclEmma 的视觉特性,hashcode() 方法显示为亮红色“未测试”。

在这个例子中,考虑到实现有多简单,是否值得费心去测试hashCode()?我觉得我会为此方法添加一个单元测试,只是为了提高代码覆盖率,并摆脱 EclEmma 在这些行中添加的刺眼的红色突出显示。

也许我有点神经质和类似强迫症,但我发现使用 EclEmma 之类的东西可以很容易地查看未测试的内容 - 该插件以红色突出显示源代码,并以绿色突出显示代码 - 确实让我希望尽可能多地获得 100% 绿色的课程 - 即使它不会增加太多好处。

【问题讨论】:

    标签: java tdd code-coverage emma


    【解决方案1】:

    我使用代码覆盖率来提示我可能有不完整的测试集的地方。例如,我可能会为某些给定功能编写测试,然后开发满足该功能的代码,但这样做实际上编写的代码比预期的要多——比如说它可能会在另一种情况下捕获异常测试不锻炼。当我使用覆盖分析器时,我可以看到我引入了没有关联测试的代码。它可以帮助我知道我何时没有编写足够的测试。

    另一方面,覆盖分析可能会导致错误的安全性。涵盖所有代码并不意味着您有足够的测试。您需要从代码应该做什么的角度考虑测试,并编写测试以确保它可以做到。最好先写测试。仅仅因为你的代码被完全覆盖并不意味着代码做了它应该做的事情。

    在您的示例中,我会在编写代码之前编写 hashCode 测试来定义该方法的功能。因此,我会覆盖它。这并不意味着我总是有 100% 的覆盖率。例如,我并不太热衷于为简单的访问器编写测试。我也可能不会测试从框架继承的父类中的方法,因为我觉得不需要测试其他人的代码。

    【讨论】:

      【解决方案2】:

      我认为使用可以选择忽略某些类型语句的库是值得的。例如,如果您有很多:

      if(logger.isDebugEnabled()) {
          logger.debug("something");
      }
      

      如果您可以关闭这些行的覆盖率计算,这将很有用。关闭普通 getter 和 setter(那些简单地设置或返回没有其他检查或副作用的成员变量)的覆盖计算也可以(可以说)有效。但是,我确实认为,如果您覆盖了 equals 和 hashcode,则应该对其进行测试。您添加了重要的功能,应该对其进行测试。

      需要明确的是,我认为上述情况应该排除在覆盖范围之外的原因是:

      • 不测试该功能是可以的。您不必将整个测试套件运行 5 次,并将日志库设置为每个日志记录级别,以确保您的所有语句都被命中。
      • 如果您执行上述操作,则会以另一种方式扭曲您的覆盖范围。如果你的 90% 的分支是if(log.isDebugEnabled()),并且你测试了所有分支但没有其他分支,看起来你有 90% 的分支覆盖率(好),而实际上,你有 0% 的非平凡分支覆盖率(坏!!)。

      【讨论】:

      • 我认为这是一个很好的观点。虽然为了处理这个问题,我做了一些我的 emma ant 目标以使用 log4j 配置,并将根记录器设置为 DEBUG,并且 appender 过滤器处于 OFF 级别(所以所有 log.debug() 语句都被命中,但没有在任何地方记录) .
      • 你仍然会遇到问题,因为如果你检查日志级别,它总是会评估为真(或假,取决于配置),所以你的分支覆盖率只有 50%。
      • 但是我没有做任何分支 - if(log.isDebugEnabled()) 没有别的 - 对吧?
      • 大多数覆盖工具仍会检查您是否测试了每个条件。所以对于那个分支,有两种可能性——它可以评估为真,或者它可以评估为假。如果它总是正确的,那么您将只有 50% 的覆盖率。
      • 从来没有人不小心从输入流中消耗了字节,或者从堆栈、迭代器或 isDebugEnabled 块中的任何东西中取出下一个项目......永远!
      【解决方案3】:

      代码覆盖率和测试覆盖率是有区别的。您应该尝试确保您的代码经过充分测试,而不是 100% 的代码覆盖率。

      考虑以下代码:

      public float reciprocal (float ex)
      {
          return (1.0 / ex) ;
      }
      

      如果您运行了一个通过值 1.0 的测试,那么您将获得 100% 的代码覆盖率(分支和语句)并且所有通过。代码明显有缺陷。

      衡量测试覆盖率更加困难,而且需要成为更好的开发人员和测试人员。

      关于 hashCode ,老实说,你应该对该方法进行单独的单元测试。我个人会确保它至少包含在一个单元集成测试中,并且不会直接测试每个访问器/修改器。您的投资回报通常太低,不足以证明您的努力是合理的。这当然是假设您有一个单元测试来确保您生成正确的哈希码。

      【讨论】:

      • 如果 reciprocal() 可以传递一个零,上面的代码只有一个缺陷。如果没有,那么代码就完成了。如果可以,那么您应该编写一个测试该场景的测试。
      【解决方案4】:

      通过有意义的测试达到 100% 的代码覆盖率可能不值得攀登,而且如前所述,100% 的代码覆盖率并不一定意味着应用程序中的所有可能条件都已满足经过测试。

      至于测试equalshashCode 和其他一些基于合同的 接口,例如ComparableSerializable,我确实包含了这些测试。 equals/hashCode 合约的正确实现很重要,equals/Comparable 也是如此。

      JUnit-Addons,尤其是

      【讨论】:

        【解决方案5】:

        现在可能不会太复杂,但是如果方法被修改,一个简单的检查来验证它是否仍然按预期工作可能会非常有用。

        既然支票应该真的很容易写,为什么不这样做呢?它有助于统计数据,也有助于稍后检查以防万一。

        此外,对于 TDD,您希望 100% 的覆盖率,因为这样您就可以确定(或非常接近)重构时不会破坏任何内容。

        【讨论】:

          【解决方案6】:

          一个和我一样被困在旧 Java1.4 中的程序员 ;)

          正如我在previous answer 中所说,所涵盖的代码未经代码测试。并且得出的结论是:从某个角度来看,提高代码覆盖率的唯一方法就是……删除代码!

          现在,关于 hashCode,将它包含在单元测试设计中以检查预期的排序机制是否得到遵守(而不是为了覆盖更多功能)是很有趣的

          【讨论】:

            【解决方案7】:

            正如我在其他地方所说,低代码覆盖率是一个问题,但高代码覆盖率并不意味着你正在编写纯金。

            如果您不关心代码覆盖率,那么我建议您需要对 equals()getValue() 进行测试 - 取决于 hashCode() 的使用方式和位置(对不起,我'我是 C# 开发人员),那么您可能想要测试它。

            最重要的部分是您的测试让您确信自己编写了正确的代码并且代码按预期工作。

            【讨论】:

              【解决方案8】:

              在这种特殊情况下,我想说,由于您正在测试 equals 方法,您还可以测试 equals 对象是否具有相等的哈希码:只需在所有 equals 预期返回 true 的情况下添加一个额外的测试.

              这将为您提供代码覆盖率,并让您相信 hashCode 确实满足其合同 :-)

              考虑到您显然可以信任 String 的 hashCode 方法,并且您永远不会更改此类,因此这只是微不足道的价值。但是如果你对你的 equals 方法有足够的怀疑来测试它,那么你应该对它有足够的怀疑来测试它和 hashCode 是否保持一致。而且,您应该始终怀疑您将来不想弄乱您以前所做的事情的假设。例如,如果有人出现并决定通过添加指针相等检查来“优化”,那么您可能还需要一整套测试让他们运行修改后的代码。否则他们会浪费时间在你做的同样担心上——我没有代码覆盖可以吗?

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2013-04-16
                • 2010-12-12
                • 2011-03-02
                • 1970-01-01
                • 2010-09-25
                • 2010-10-05
                • 2010-09-16
                相关资源
                最近更新 更多