8

我最近开始使用代码覆盖工具(特别是 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% 绿色的课程——即使它不会增加太多好处。

4

8 回答 8

8

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

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

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

于 2008-10-31T14:06:43.730 回答
6

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

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

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

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

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

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

考虑以下代码:

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

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

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

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

于 2008-10-31T14:32:07.593 回答
3

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

至于 testingequalshashCode其他一些基于契约的接口,比如Comparableand Serializable,我确实包含了这些测试。equals/hashCode正确执行合同很重要,同样与equals/Comparable.

请参阅JUnit-Addons,尤其是

于 2008-10-31T14:37:09.423 回答
2

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

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

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

于 2008-10-31T14:06:26.803 回答
1

一个和我一样的程序员在旧的 Java1.4 中卡住了;)

正如我之前的回答中所说,所涵盖的代码未经代码测试。结论是:从某个角度来看,提高代码覆盖率的唯一方法是……删除代码!

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

于 2008-10-31T14:12:22.470 回答
0

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

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

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

于 2008-10-31T14:07:46.037 回答
0

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

这将为您提供代码覆盖率,并且在讨价还价中,它将使您对 hashCode 确实满足其合同有信心:-)

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

于 2008-10-31T16:20:29.263 回答