您在多大程度上接受了代码覆盖?

时间:2008-10-31 14:01:11

标签: java tdd code-coverage emma

我最近开始使用代码覆盖工具(特别是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添加到这些行中的明显红色突出显示。

也许我是神经质和OCD一样,但我发现使用像EclEmma之类的东西可以很容易地看到未经测试的东西 - 插件突出显示红色的源代码,并覆盖绿色代码 - 真的让我想尽可能地推动尽可能多的课程100%绿色 - 即使它没有增加太多的好处。

8 个答案:

答案 0 :(得分:8)

我使用代码覆盖率给出了我可能有一组不完整测试的地方的提示。例如,我可能会为某些给定的功能编写一个测试,然后开发满足该功能的代码,但这样做实际上编写了比预期更多的代码 - 假设它可能在备用情况下捕获异常测试没有运动。当我使用覆盖率分析器时,我可以看到我引入了没有相关测试的代码。它帮助我知道我什么时候没有编写足够的测试。

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

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

答案 1 :(得分:6)

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

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

如果您可以关闭这些行的覆盖率计算,这将非常有用。对于无关紧要的getter和setter(仅设置或返回成员变量而没有其他检查或副作用)的覆盖计算也可以(可以说)有效。但我认为,如果你已经覆盖了equals和hashcode,那么应该对它们进行测试。您添加了非平凡的功能,应该进行测试。

为了清楚起见,我认为上述情况应该从保险范围中排除的原因是:

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

答案 2 :(得分:4)

代码覆盖率和测试覆盖率之间存在差异。您应该尝试确保您的代码经过充分测试,而不是100%的代码覆盖率。

请考虑以下代码:

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

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

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

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

答案 3 :(得分:3)

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

至于测试equalshashCode和其他一些 基于合同的 界面,例如ComparableSerializable ,我确实包括那些测试。正确实施equals/hashCode合同非常重要,与equals/Comparable同样如此。

请参阅JUnit-Addons,尤其是

答案 4 :(得分:2)

现在可能不会太复杂,但如果方法被修改,稍后检查以确认它仍然按预期工作可能会非常有用。

由于检查应该很容易编写,为什么不这样做呢?它可以帮助统计数据,也可以帮助您稍后检查,以防它破裂。

此外,对于TDD,您希望100%覆盖,因为您可以确定(或非常接近它)您在重构时没有破坏任何内容。

答案 5 :(得分:1)

一位同事程序员在旧的Java1.4中像我一样陷入困境;)

如我previous answer中所述,所涵盖的代码未经过代码测试。结论是:从某一点来看,改善代码覆盖率的唯一方法是......删除代码!

现在,关于hashCode,有趣的是它在单元测试设计中被覆盖以检查预期的排序机制是否被尊重(而不是用于覆盖另一个函数)

答案 6 :(得分:0)

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

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

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

答案 7 :(得分:0)

在这种特殊情况下,我会说,既然你正在测试equals方法,你也可以测试相等的对象是否具有相同的哈希码:只需在所有等于预期返回true的情况下添加额外的测试

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

鉴于你显然可以信任String的hashCode方法,并且你不希望永远改变这个类,这只是勉强值得的。但是如果你对你的equals方法有足够的怀疑来测试它,那么你应该怀疑它是否可以测试它和hashCode保持一致。而且你应该始终怀疑你以后不会想要弄乱你以前做过的假设。例如,如果有人出现并决定通过添加指针等式检查来“优化”,那么您也可以为他们提供一整套测试来运行修改后的代码。否则他们会把时间浪费在你所做的同样的担心上 - 我没有代码覆盖吗?