Jacoco 代码覆盖率随着迁移到 Java11 而下降

Jacoco code coverage dropped with migration to Java 11

我有几个使用 Java 8 构建的 Gradle 项目,在最近将它们转换为使用 Java 11 之后,Jacoco 代码覆盖率报告报告的百分比比前。在一个项目中,在转换之后,我的覆盖率立即从 81% 下降到 16%。

我尝试将 Jacoco 插件更新到 0.8.3(官方 JDK 11 support),Gradle 更新到 5.4,TestNG 更新到 6.14.3(不确定是否有任何效果;认为使用最新版本不会有什么坏处)。即使在这些变化之后,我上面提到的项目也有 16% 的覆盖率。我手动检查了几个报告覆盖率为 0% 的 类,发现它们实际上确实有测试覆盖率。

例如,我将此方法添加到我的其中一个 类:

public String helloWorld(){
        return "hello";
    }

然后我在测试中使用了它:

@Test(groups = IntegrationTest.INTEGRATION_GROUP)
    public void testHelloWorld() {
        String helloWorld = authManager.helloWorld();
        assertEquals(helloWorld, "hello");
    }

覆盖率报告为 0:

如果有帮助,这是我的 Jacoco Gradle 设置。我正在使用自定义插件来配置它们。

  class ManagedJacocoPlugin implements ManagedPlugin {
  @Override
  void apply(PluginManager pluginManager) {
    pluginManager.apply(JacocoPlugin.class)
  }

  @Override
  void configure(Project project, GradlePluginConfig pluginConfig) {
    def jacoco = project.extensions.getByName("jacoco")
    jacoco.toolVersion = "0.8.3"

    def jacocoTestReport = project.tasks.getByName('jacocoTestReport')
    jacocoTestReport.reports {
      xml.enabled false
      csv.enabled false
    }

    project.tasks.withType(Test).each { t ->
      t.jacoco {
        destinationFile = project.file("$project.buildDir/jacoco/test.exec")
      }
    }

    jacocoTestReport.dependsOn "integrationTest"
  }
}

据我所知,考虑到我使用的工具版本,Java 11 应该完全支持 Jacoco 的覆盖范围。我在这里错过了什么?

以下是第 https://whosebug.com/help/mcve 页关于如何创建最小、完整且可验证的示例的内容:

Make sure it's complete

Copy the code from your question into a new file or project, then run it. If it doesn't run for you, then it won't run for anyone else.

但是谁知道你的例子中的 ManagedPlugin 是什么?

但是好吧,让我们尝试遵循上述建议并使用我们所拥有的,假装我们有时间猜测并且我们很幸运能够猜对。

ManagedPlugin外的所有内容在添加许多缺失部分后变为以下build.gradle

apply plugin: 'java'
apply plugin: 'jacoco'

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'org.testng', name: 'testng', version: '6.14.3'
}

test {
    useTestNG() {
        includeGroups('unit')
    }
}

task integrationTest(type: Test, dependsOn: ['test']) {
    useTestNG() {
        includeGroups('integration')
    }
}


def jacoco = project.extensions.getByName("jacoco")
jacoco.toolVersion = "0.8.3"

def jacocoTestReport = project.tasks.getByName('jacocoTestReport')
jacocoTestReport.reports {
    xml.enabled false
    csv.enabled false
}

project.tasks.withType(Test).each { t ->
    t.jacoco {
        destinationFile = project.file("$project.buildDir/jacoco/test.exec")
    }
}

jacocoTestReport.dependsOn "integrationTest"

方法helloWorld进入src/main/Example.java

class Example {
    public String helloWorld() {
        return "hello";
    }
}

方法testHelloWorld进入src/test/ExampleTest.java

import org.testng.annotations.Test;
import static org.testng.Assert.*;

class ExampleTest {
    Example authManager = new Example();

    @Test(groups = "integration")
    public void testHelloWorld() {
        String helloWorld = authManager.helloWorld();
        assertEquals(helloWorld, "hello");
    }
}

使用 Gralde 5.4 和 JDK 11.0.1 执行 gradle clean jacocoTestReport 生成以下报告

因此我们可以得出结论,所提供的示例肯定是不完整的。

让我们再猜一猜并加入src/main/java/Example.java

    public void anotherMethod() {
    }

并进入src/test/java/ExampleTest.java

    @Test(groups = "unit")
    public void test() {
       new Example().anotherMethod();
    }

现在执行 gradle clean jacocoTestReport 会生成以下报告

看来我们现在可以重现您的问题了。

为什么anotherMethod没有被覆盖?让我们听从 https://whosebug.com/help/mcve 的另一个好建议:

Divide and conquer. When you have a small amount of code, but the source of the problem is entirely unclear, start removing code a bit at a time until the problem disappears – then add the last part back.

这不仅适用于代码,还适用于版本更改 - 让我们尝试将 Gradle 版本的更改从 5.4 逆转回 4.10.3 并执行 gradle clean jacocoTestReport 产生

因此我们可以得出结论,Gradle 中的某些内容已更改。让我们检查一下它的更新日志——https://docs.gradle.org/5.0/release-notes.html 包含一个非常有趣的声明:

JaCoCo plugin now works with the build cache and parallel test execution

... the tasks running with code coverage are configured to delete the execution data just before they starts executing ...

任务 integrationTest 删除任务 test 收集的数据。让我们尽量不要使用相同的文件:

//project.tasks.withType(Test).each { t ->
//    t.jacoco {
//        destinationFile = project.file("$project.buildDir/jacoco/test.exec")
//    }
//}

jacocoTestReport.executionData(test)
jacocoTestReport.executionData(integrationTest)

现在执行 gradle clean jacocoTestReport 即使 Gradle 5.4 也会产生