测量Java 8代码的条件覆盖范围是否有意义?


19

我想知道自Java 8出现以来,当前使用Java的工具来衡量条件代码覆盖率是否还没有过时。使用Java 8 OptionalStream我们通常可以避免代码分支/循环,这使得在不测试所有可能的执行路径的情况下轻松获得很高的条件覆盖率。让我们将旧的Java代码与Java 8代码进行比较:

在Java 8之前:

public String getName(User user) {
    if (user != null) {
        if (user.getName() != null) {
            return user.getName();
        }
    }
    return "unknown";
}

以上方法有3条可能的执行路径。为了获得100%的条件覆盖率,我们需要创建3个单元测试。

Java 8:

public String getName(User user) {
    return Optional.ofNullable(user)
                   .map(User::getName)
                   .orElse("unknown");
}

在这种情况下,分支是隐藏的,我们只需要进行一次测试即可获得100%的覆盖率,无论哪种情况我们都将进行测试。我相信,尽管仍然存在相同的三个逻辑分支。我认为这使有条件覆盖范围统计这些天完全不可信。

测量Java 8代码的条件覆盖范围是否有意义?还有其他工具可以发现未经测试的代码吗?


5
覆盖率度量从来不是确定代码是否经过良好测试的好方法,而只是确定测试内容的一种方法。一个好的开发人员会仔细考虑自己的各种情况,并为所有这些情况(或至少她认为重要的所有情况)设计测试。
kdgregory

3
当然,高条件覆盖率并不意味着我们已经进行了良好的测试,但是我认为知道哪些执行路径被发现是最大的优势,而这正是问题所在。没有条件覆盖,发现未经测试的情况就困难得多。关于路径:[user:null],[user:notnull,user.name:null],[user:notnull,user.name:notnull]。我想念的是什么?
Karol Lewandowski

6
什么合同getName?似乎是如果user为null,则应返回“未知”。如果user不为null且user.getName()为null,则应返回“未知”。如果user不为null user.getName()且不为null,则应返回该值。因此,您将对这三种情况进行单元测试,因为这就是合同的getName内容。您似乎在向后做。您不想看到分支并根据那些分支编写测试,您不想根据合同编写测试,并确保合同已满。到那时,您的覆盖范围就会很好。
Vincent Savard

1
再说一次,我并不是说覆盖范围证明我的代码已经过完美测试,但这是极其有用的工具,向我展示了我尚未测试的内容。我认为测试合同与测试执行路径是分不开的(您的示例非常出色,因为它涉及隐式语言机制)。如果您尚未测试路径,那么您还没有完全测试合同或未完全定义合同。
Karol Lewandowski

2
我将重复我先前的观点:情况总是如此,除非您仅将自己限制在基本语言功能上,并且永远不要调用任何尚未检测的功能。这意味着没有第三方库,也没有使用SDK。
kdgregory,2016年

Answers:


4

是否有任何工具可以测量可在Java 8中创建的逻辑分支?

我什么都不知道。我尝试通过JaCoCo(aka EclEmma)运行您拥有的代码,只是为了确保它在Optional版本中显示0个分支。我不知道有任何配置它的方法可以这样说。如果将其配置为还包含JDK文件,则理论上它将显示中的分支Optional,但我认为开始验证JDK代码将很愚蠢。您只需要假设它是正确的即可。

但是,我认为核心问题是意识到从某种意义上讲,Java 8之前的其他分支是人为创建的分支。它们不再存在于Java 8中,仅意味着您现在拥有适合该工作的正确工具(在本例中为Optional)。在Java 8之前的代码中,您必须编写额外的单元测试,以便可以确信每个代码分支的行为都可以接受-这在不像User/ 那样琐碎的代码部分中变得更加重要。getName例。

在Java 8代码中,您对JDK确信代码可以正常工作充满信心。照Optional原样,您应该像对待代码覆盖率工具那样对待该行:3行和0个分支。下面的代码中还有其他行和分支,这是您之前从未注意的,但是每次使用诸如ArrayListor或时都存在HashMap


2
“它们不再存在于Java 8中……”-我不同意,Java 8是向后兼容的,if并且null仍然是该语言的一部分;-)仍然可以用旧的方式编写代码并传递null用户或具有null名称的用户。您的测试应仅证明已遵守合同,无论如何实施方法。关键是没有工具可以告诉您是否已完全测试合同。
Karol Lewandowski

1
@KarolLewandowski我认为Shaz在说的是,如果您相信Optional(和相关方法)如何工作,则不再需要测试它们。您测试的方式不同if-else:每个人if都是潜在的雷区。Optional并且已经编写了类似的功能性惯用语,并保证不会让您绊倒,因此从本质上讲,这是一个“分支”消失了。
Andres F.

1
@AndresF。我不认为Karol建议我们进行测试Optional。就像他说的那样,从逻辑getName()上讲,无论其实现如何,我们仍然应该以预期的方式测试它是否可以处理各种可能的输入。没有代码覆盖工具来帮助实现JDK8之前的方式,很难确定这一点。
迈克·帕特里奇

1
@MikePartridge是的,但要点是,这不是通过分支机构覆盖范围来完成的。编写时需要分支覆盖,if-else因为这些构造中的每一个都是特别的。相比之下,OptionalorElsemap,等,都已经测试。实际上,当您使用更强大的惯用语时,分支“消失”。
安德列斯·F
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.