Java Gradle:如何使用 jacoco 生成集成测试的覆盖率报告

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19025138/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 13:28:43  来源:igfitidea点击:

Gradle : How to generate coverage report for Integration test using jacoco

javagradlejacoco

提问by Veera

I am new to gradle. I am using the below code. But it generates coverage for unit test cases. But it didn't generate for integration test cases. I have my test classes in the package src/test/java.

我是 gradle 的新手。我正在使用下面的代码。但它会生成单元测试用例的覆盖率。但它没有为集成测试用例生成。我在包 src/test/java 中有我的测试类。

test {
    dependsOn jettyRunWar
    ignoreFailures true
    finalizedBy jettyStop
}

apply plugin: 'jacoco'

jacocoTestReport {
    group = "Reporting"
    description = "Generate Jacoco coverage reports after running tests."
    additionalSourceDirs = files(sourceSets.main.allJava.srcDirs)
}

回答by AKS

It seems like, what you need to tell build.gradle is where are your Intergration tests (i.e. folder containing those IT tests) using sourceSets. In my case, i have source under src/java (instead of src/main/java - gradle default).. my unit tests (Junit) under test/java folder, and my integration tests under src/java-test folder.

看起来,您需要告诉 build.gradle 是使用 sourceSets 的集成测试(即包含这些 IT 测试的文件夹)在哪里。就我而言,我在 src/java 下有源代码(而不是 src/main/java - gradle 默认)。我的单元测试(Junit)在 test/java 文件夹下,我的集成测试在 src/java-test 文件夹下。

sourceSets {
   main {
      java {
         srcDir 'src/java'
      }
   }
   test {
      java {
         srcDir 'test/java'
      }
      resources {
         srcDir 'test/resources'
         srcDir 'conf'
      }
   }
   integrationTest {
      java {
         srcDir 'src/java-test'
      }
   }
}

Then, I have integrationTest task as ... which you can tweak as you might not have cleanTest (custom task that i have created), so you can ignore that dependsOn... i think in your case you'll use something like jettyStart as you're using that for IT tests (starting the container for running IT tests and then finalizedBy feature to stop jetty .. jetty plugin)

然后,我将 integrationTest 任务作为...您可以进行调整,因为您可能没有 cleanTest(我创建的自定义任务),因此您可以忽略该dependsOn...我认为在您的情况下,您将使用诸如 jettyStart 之类的东西当您将其用于 IT 测试时(启动容器以运行 IT 测试,然后 finalizedBy 功能以停止 jetty .. jetty 插件)

task integrationTest( type: Test, dependsOn: cleanTest ) {
   jacoco {
      //destinationFile = file("$buildDir/jacoco/jacocoTest.exec")
      destinationFile = file("$buildDir/jacoco/integrationTest.exec")
      //classDumpFile = file("$buildDir/jacoco/classpathdumps")
      classDumpFile = file("$buildDir/classes/integrationTest")
   }
   testClassesDir = sourceSets.integrationTest.output.classesDir
   classpath = sourceSets.integrationTest.runtimeClasspath
}

SEE this post for more detailed output structure and script that I have at my end. Im getting the .exec for both Unit tests (test.exec) and IT tests intergrationTest.exec.. but Im not getting the jacoco.xml/jacocoHtml reports for both tests. I also found that, if I run "gradle clean build" (which includes call to "test" task) and "gradle clean build integrationTest" then, later one overwrites unit tests data in build/test-results folder and build/reports/tests folder.

请参阅这篇文章,了解我最后拥有的更详细的输出结构和脚本。我得到了单元测试 (test.exec) 和 IT 测试 intergrationTest.exec 的 .exec .. 但我没有得到两个测试的 jacoco.xml/jacocoHtml 报告。我还发现,如果我运行“gradle clean build”(包括对“test”任务的调用)和“gradle clean build integrationTest”,那么稍后会覆盖 build/test-results 文件夹和 build/reports/ 中的单元测试数据测试文件夹。

Jacoco Unit and Integration Tests coverage - individual and overall

Jacoco 单元和集成测试覆盖范围 - 个人和整体

NOTE: in my case, jacocoTestReport is defined in the global gradle init.d folder in one of the common gradle file. This will help us not to include the same code in all / at project level build.gradle file.

注意:就我而言,jacocoTestReport 是在通用 gradle 文件之一的全局 gradle init.d 文件夹中定义的。这将帮助我们不在所有 / 在项目级别的 build.gradle 文件中包含相同的代码。

回答by Brice

Using Gradle 5.4.1 (and now 5.5.1), I was able to get a report after any test task, currently I have both testand integrationTesttasks.

使用 Gradle 5.4.1(现在是 5.5.1),我能够在任何测试任务后获得报告,目前我有testintegrationTest任务。

EDIT3: Fixed a potential bug when executing only some test tasks

EDIT3:修复了仅执行某些测试任务时的潜在错误

  • Don't configure executionDatain doLast/ doFirstblocks, it was an error from my part. For more information checks this gradle github ticket
  • Added the more prudent alternative (again not in a doLast/ doFirstblock) executionData { tasks.withType(Test).findAll { it.jacoco.destinationFile.exists() }*.jacoco.destinationFile }
  • 不要executionDatadoLast/doFirst块中配置,这是我的错误。有关更多信息,请查看此gradle github 票证
  • 添加了更谨慎的选择(再次不在doLast/doFirst块中) executionData { tasks.withType(Test).findAll { it.jacoco.destinationFile.exists() }*.jacoco.destinationFile }

EDIT2: The solution is the same, I just tweaked

EDIT2:解决方案是一样的,我只是调整

  • the reports destination to use jacoco.reportsDir,
  • the executionData now takes tasks.withType(Test)instead of just [test, integrationTest]
  • setting executionDatais done in the doFirstblock instead of doLast
  • 要使用的报告目的地jacoco.reportsDir
  • executionData 现在需要,tasks.withType(Test)而不仅仅是[test, integrationTest]
  • 设置executionDatadoFirst块中完成而不是doLast

EDIT: After looking at the documentation of JacocoReport, there's a variant JacocoReport:executionDatathat take Gradle tasks directly. It works because the JaCoCo plugin adds a JacocoTaskExtensionextension to all tasks of type Test. Which is then less error prone.

编辑:查看文档后JacocoReport,有一个变体JacocoReport:executionData直接执行Gradle 任务。它有效是因为JaCoCo 插件JacocoTaskExtension为所有类型的任务添加了一个扩展Test。这样就不太容易出错了。



jacocoTestReport {
    // The JaCoCo plugin adds a JacocoTaskExtension extension to all tasks of type Test.
    // Use task state to include or not task execution data
    // https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/TaskState.html
    // This declaration will be used as a closure, notice there no wrapping parenthesis
    executionData tasks.withType(Test).findAll { it.state.executed }

    // If the above instruction really don't work, there maybe some things that intervene in the process, in this case, you may be a bit more lucky with this instruction
    // executionData { tasks.withType(Test).findAll { it.jacoco.destinationFile.exists() }*.jacoco.destinationFile }

    reports {
        xml.enabled true
        xml.destination(file("${jacoco.reportsDir}/all-tests/jacocoAllTestReport.xml"))
        html.enabled true
        html.destination(file("${jacoco.reportsDir}/all-tests/html"))
    }
}

And the same trick can be applied to sonarqubetask :

同样的技巧可以应用于sonarqubetask :

sonarqube {
    group = "verification"
    properties {
        // https://jira.sonarsource.com/browse/MMF-1651
        property "sonar.coverage.jacoco.xmlReportPaths", jacocoTestReport.reports.xml.destination
        properties["sonar.junit.reportPaths"] += integrationTest.reports.junitXml.destination
        properties["sonar.tests"] += sourceSets.integrationTest.allSource.srcDirs
        // ... other properties
    }
}


Older but very working answer. Also using the knowledge above (that Tests task are extended by JacocoTaskExtension) it's possible to replace the manual fileconfiguration of executionDataby test.jacoco.destinationFileand integrationTest.jacoco.destinationFile.

较旧但非常有效的答案。同样使用上述知识(该Test任务由 扩展JacocoTaskExtension),可以替换由和的手动file配置。executionDatatest.jacoco.destinationFileintegrationTest.jacoco.destinationFile

// Without it, the only data is the binary data, 
// but I need the XML and HTML report after any test task
tasks.withType(Test) {
    finalizedBy jacocoTestReport
}

// Configure the report to look for executionData generated during the test and integrationTest task
jacocoTestReport {
    executionData(file("${project.buildDir}/jacoco/test.exec"),
                  file("${project.buildDir}/jacoco/integrationTest.exec"))
    reports {
        // for sonarqube
        xml.enabled true
        xml.destination(file("${project.buildDir}/reports/jacoco/all-tests/jacocoAllTestReport.xml"))
        // for devs
        html.enabled true
        html.destination file("${project.buildDir}/reports/jacoco/all-tests/html")
    }
}


sonarqube {
    group = "verification"
    properties {
        // https://jira.sonarsource.com/browse/MMF-1651
        property "sonar.coverage.jacoco.xmlReportPaths", ${project.buildDir}/test-results/integrationTest"
        properties["sonar.junit.reportPaths"] += "${project.buildDir}/test-results/integrationTest"
        properties["sonar.tests"] += sourceSets.integrationTest.allSource.srcDirs
        // ... other properties
    }
}

project.tasks["sonarqube"].dependsOn "jacocoTestReport"

回答by user2738882

I believe the most full answer will look like:

我相信最完整的答案如下:

tasks.withType(Test) {
    finalizedBy jacocoTestReport
}

project.jacocoTestReport {
    getExecutionData().setFrom(fileTree(buildDir).include("/jacoco/*.exec"))

    reports {
        csv.enabled true
    }
}

At least it's fully suited for my needs with integration and functional testing.

至少它完全适合我的集成和功能测试需求。

回答by Leo Gerber

Since I couldn't make it run with any of the answers, I will add my solution here. It will work if you run your test task (e.g. integTest) first and call the jacocoTestReportafterwards.

由于我无法使用任何答案运行它,因此我将在此处添加我的解决方案。如果您首先运行您的测试任务(例如integTest)然后调用它,它将起作用jacocoTestReport

All you need is to tell the jacocoTestReporttask where to find the gathered execution data from you test task. The execution data is always named after the test task. So if you have a test task called integTest, your execution data will be stored in build/jacoco/integTest.exec. The jacocoTestReporttask can be configured to look for those other files too by adding them to the property executionData. You can also add wildcards includes so all execution data is taken into consideration:

您只需要告诉jacocoTestReport任务在哪里可以找到从您的测试任务收集的执行数据。执行数据总是以测试任务命名。因此,如果您有一个测试任务被调用integTest,您的执行数据将存储在build/jacoco/integTest.exec. 通过将jacocoTestReport任务添加到属性 executionData 中,也可以将任务配置为查找这些其他文件。您还可以添加通配符包含,以便考虑所有执行数据:

jacocoTestReport {
    executionData = fileTree(dir: project.projectDir, includes: ["**/*.exec"])
}

by executing the statement below the test coverage jacoco report will be created for you integration test task (e.g. integTest)

通过执行下面的语句,将为您创建集成测试任务(例如integTest

./gradlew integTest jacocoTestReport

This also works for multi module projects where you want to run the integTesttask in module a:

这也适用于要integTest在 module 中运行任务的多模块项目a

./gradlew a:integTest a:jacocoTestReport