为什么Intellij代码覆盖率和jacoco代码覆盖率显示的百分比不同?

15

我在IntelliJ中有一个Gradle项目(Java)。我右键单击IntelliJ中的项目,然后运行Run Tests in projectName with coverage,它会在右侧创建一些测试报告。在那个右侧上,我有一些数字,比如:

| Class, %   | Method, %   | Line, %
--------------------------------------
80%(80/100)  50%(100/200)  30%(30/100)

注意:上述数字仅为示例,不是真实的。

现在我打开命令行并运行了gradlew jacocoTestReport,得到了一个不同的数字集用于方法和行,但类数字相同。为什么会出现这种差异?

是否有一种方法可以从命令行而不是右键单击来运行IntelliJ的代码覆盖率?

我只想知道IntelliJ是否使用与Jacoco不同的方式来计算这些数字。但即使是这种情况,我的假设也是只有一种计算东西的方法,对吗?或者IntelliJ或Jacoco没有计算包含Lombok注释等的类,从而减少了最终计数中的方法(getter和setter)数量?


分子和分母与方法和行有何不同?您可以轻松检查IntelliJ的方法和行。我不知道Jacoco。我相信IntelliJ。我已经看到其他代码覆盖工具(如SonarCube)由于必须明确告知排除测试类别统计数据而导致差异。 - duffymo
2
我的问题中的数字只是举例。但是问题是,Jacoco比Intellij的代码运行器有更多的方法和更多的行。我认为Intellij的代码运行器没有计算lombok注释等。如果我为一个有2个字段的类添加@Getter,在运行时这个类将生成这两个方法。也许Jacoco正在计算这些类,而Intellij没有?其他库注释也是一样的。 - theprogrammer
2个回答

8

我在一项相关任务中偶然发现了这个问题,虽然它已经很旧了,但我认为它可能仍然对某些人有趣。

方法数量

主要问题是Intellij覆盖率和Jacoco是否以不同的方式计算数字,哪种方式是正确的。简短的答案是:Intellij覆盖率摘要使用开发人员直接提供的方法,而Jacoco在字节码级别上操作并显示那里发现的方法数量。为了证明这一点,我创建了一个包含四个方法的简单类:

public class Exp {

    private final LinkedList<Integer> vals = new LinkedList<>();

    public void addVal(int v) {
        vals.add(v);
    }

    public List<Integer> doubled() {
        return vals.stream().map(x -> x*2).collect(Collectors.toList());
    }

    public List<Integer> evens() {
        return vals.stream().filter(x -> x%2 == 0).collect(Collectors.toList());
    }

    public static void main(String[] args) {
        Exp t = new Exp();

        t.addVal(1);
        t.addVal(2);
        t.addVal(3);

        System.out.println(t.doubled());
        System.out.println(t.evens());
    }
}

在Intellij摘要中,右侧显示以下数值:

intellij coverage summary

所以方法的数量等于示例代码中的方法数量。Jacoco报告了七种方法,如报告中所示(与Eclipse 2020-09中的Emma插件相同): jacoco test report 这是我们可以在字节码中找到的方法数量,例如通过使用javap反汇编器命令。在这里,我们可以看到两个lambda表达式被实现为类的方法,并且还插入了一个标准构造函数。
C:\_workspace\intellij\Tests\out\production\mod>javap -p Exp.class
Compiled from "Exp.java"
public class Exp {
  private final java.util.LinkedList<java.lang.Integer> vals;
  public Exp();
  public void addVal(int);
  public java.util.List<java.lang.Integer> doubled();
  public java.util.List<java.lang.Integer> evens();
  public static void main(java.lang.String[]);
  private static boolean lambda$evens$1(java.lang.Integer);
  private static java.lang.Integer lambda$doubled$0(java.lang.Integer);
}

我有点困惑的是,IntelliJ 覆盖率报告(Run->Generate Corevage Report)显示了五个方法:

intellij coverage report

添加一个标准构造函数到代码中并重新生成报告后,报告包含了生成的标准构造函数,但不包括lambda表达式。似乎存在一种中间计数方法。
至于Intellij或Jacoco哪个是正确的问题,我会说他们都是正确的,这只是一个定义的问题。
行号:
在我的测试中,所有报告显示的行号都是一致的。在上面的例子中,报告了13行可执行代码。我对Intellij覆盖率摘要中行数的印象是它并不能始终正确地刷新。可能需要进行干净的重建。

我只想补充一点,对于更大的文件,覆盖率数字会有所不同,使用Eclipse和Intellij的开发团队会感到困惑。我有一个SDK,其中jacoco / Eclipse显示覆盖率为68%,而IntelliJ则显示覆盖率为80%以上。这很糟糕也很恼人。如果我没记错,Sonar和Codecov等工具会解析jacoco报告,因此IntelliJ的覆盖率毫无用处。 - AndrasCsanyi
@AndrasCsanyi 为什么说Intellij的代码覆盖率无用?首先,它更容易设置,因为它与IDE集成,所以不会给项目增加任何样板代码。我发现Intellij的代码覆盖率实际上提供了你所需要的信息,即该行是否被执行到,而不是作为字节码是否被执行到。检查行的执行有效性取决于测试。 - undefined

-1
我注意到我使用的@Test注释来自org.junit.jupiter.api.Test,但Jacoco没有检测到它。将导入更改为import org.junit.Test解决了我的覆盖问题。

7
org.junit.jupiter.api.Test 切换到 org.junit.Test 意味着你从 JUnit 5 切换到了 JUnit 4,这可能不是正确的解决方案。 - Mark Rotteveel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接