使用 Gradle 过滤 JaCoCo 覆盖率报告

Filter JaCoCo coverage reports with Gradle

问题:

我有一个包含 的项目,我希望能够过滤某些 类 and/or 包。

相关文档:

我已阅读以下文档:

官方网站: http://www.eclemma.org/jacoco/index.html

的官方 文档: https://gradle.org/docs/current/userguide/jacoco_plugin.html

官方 Github 问题,正在处理报道: https://github.com/jacoco/jacoco/wiki/FilteringOptions https://github.com/jacoco/jacoco/issues/14

相关 Whosebug 链接:

JaCoCo & Gradle - Filtering Options(无人回答)

Exclude packages from Jacoco report using Sonarrunner and Gradle (Not using )

JaCoCo - exclude JSP from report (It seems to work for , I am using )

(It seems to work for , I am using )

JaCoCo gradle plugin exclude(无法正常工作)

Gradle Jacoco - coverage reports includes classes excluded in configuration(看起来很接近,用了doFirst,对我没用)

我尝试过的示例:

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

buildscript {
    repositories {
        mavenCentral()
        jcenter()
    }
}

repositories {
    jcenter()
}

jacocoTestReport {
    reports {
        xml {
            enabled true // coveralls plugin depends on xml format report
        }

        html {
            enabled true
        }
    }

    test {
        jacoco {
            destinationFile = file("$buildDir/jacoco/jacocoTest.exec")
            classDumpFile = file("$buildDir/jacoco/classpathdumps")
            excludes = ["projecteuler/**"] // <-- does not work
            // excludes = ["projecteuler"]
        }
    }
}

问题:

如何在生成 覆盖率报告时排除某些包和 类?

感谢,Yannick Welsch

在搜索 Google、阅读 Gradle 文档并浏览旧的 Whosebug 帖子后,我在官方 论坛上找到了这个答案!

jacocoTestReport {
    afterEvaluate {
        classDirectories.setFrom(files(classDirectories.files.collect {
            fileTree(dir: it, exclude: 'com/blah/**')
        }))
    }
}

来源: https://issues.gradle.org/browse/GRADLE-2955

对于较早的 gradle 版本 < 5.x 可能需要使用 classDirectories = files(classDirectories.files.collect { 而不是 classDirectories.setFrom

我的 build.gradle 项目 Java/Groovy 的解决方案:

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

jacocoTestReport {
    reports {
        xml {
            enabled true // coveralls plugin depends on xml format report
        }

        html {
            enabled true
        }
    }

    afterEvaluate {
        classDirectories = files(classDirectories.files.collect {
            fileTree(dir: it,
                    exclude: ['codeeval/**',
                              'crackingthecode/part3knowledgebased/**',
                              '**/Chapter7ObjectOrientedDesign**',
                              '**/Chapter11Testing**',
                              '**/Chapter12SystemDesignAndMemoryLimits**',
                              'projecteuler/**'])
        })
    }
}

如您所见,我成功地向 exclude: 添加了更多内容以过滤一些包。

来源: https://github.com/jaredsburrows/CS-Interview-Questions/blob/master/build.gradle

其他项目的自定义任务,例如 Android:

apply plugin: 'jacoco'

task jacocoReport(type: JacocoReport) {
    reports {
        xml {
            enabled true // coveralls plugin depends on xml format report
        }

        html {
            enabled true
        }
    }

    afterEvaluate {
        classDirectories = files(classDirectories.files.collect {
            fileTree(dir: it,
                    exclude: ['codeeval/**',
                              'crackingthecode/part3knowledgebased/**',
                              '**/Chapter7ObjectOrientedDesign**',
                              '**/Chapter11Testing**',
                              '**/Chapter12SystemDesignAndMemoryLimits**',
                              'projecteuler/**'])
        })
    }
}

来源: https://github.com/jaredsburrows/android-gradle-java-app-template/blob/master/gradle/quality.gradle#L59

Here 是 ANT 中针对此问题的解决方案。这可以通过在 jacocoTestReport 任务下添加以下内容来适应 gradle。尽管 jacoco 并未真正记录这一点,但它似乎是目前过滤测试结果的唯一方法。

afterEvaluate {
    classDirectories = files(classDirectories.files.collect {
        fileTree(dir: it, exclude: 'excluded/files/**')
    })
}

这已经有一段时间了,但我只是 运行 了解了这一点。我正在为所有需要的排除而苦苦挣扎。我发现这对我来说要简单得多。如果你遵循 Maven 项目布局风格 /src/main/java 和 /src/test/java,你只需要把 buildDir/classes/main 在你的 classDirectories 配置中像这样:

afterEvaluate {
    jacocoTestReport {
        def coverageSourceDirs = ['src/main/java']
        reports {
            xml.enabled false
            csv.enabled false
            html.destination "${buildDir}/reports/jacocoHtml"
        }
        sourceDirectories = files(coverageSourceDirs)
        classDirectories = fileTree(
                dir: "${project.buildDir}/classes/main",
                excludes: [
                      //whatever here like JavaConfig etc. in /src/main/java
                     ]
        )
    }
}

对我来说,与

一起工作很好
test {
  jacoco {
    excludes += ['codeeval/**',
                 'crackingthecode/part3knowledgebased/**',
                 '**/Chapter7ObjectOrientedDesign**',
                 '**/Chapter11Testing**',
                 '**/Chapter12SystemDesignAndMemoryLimits**',
                 'projecteuler/**']
  }
}

如文档中所述 https://docs.gradle.org/current/userguide/jacoco_plugin.html#N16E62 最初被问到 所以答案是:

所以如果你问我:这不是

的问题
excludes = ["projecteuler/**"]

excludes += ["projecteuler/**"]

但是

excludes = ["**/projecteuler/**"]

排除包*.projecteuler.*

test {} 在项目级别,未嵌套在 jacocoTestReport

下面的代码也将 类 排除在覆盖验证之外:

jacocoTestCoverageVerification {
    afterEvaluate {
        classDirectories = files(classDirectories.files.collect {
            fileTree(dir: "${project.buildDir}/classes/main",
                    exclude: ['**/packagename/**'])
        })
    }
}

对于 Gradle 版本 5.x,classDirectories = files(...) 给出弃用警告并且从 Gradle 6.0 开始根本不起作用 这是不推荐使用的排除 类:

的方式
jacocoTestReport {
    afterEvaluate {
        classDirectories.setFrom(files(classDirectories.files.collect {
            fileTree(dir: it, exclude: 'com/exclude/**')
        }))
    }
}

在 gradle.properties 文件中添加以下配置

coverageExcludeClasses=["com.example.package.elasticsearch.*", "com.example.package2.*",]

一些评论提到了弃用警告。解决只需使用 getter:

afterEvaluate {
    getClassDirectories().from(files(classDirectories.files.collect {
        fileTree(dir: it, exclude: 'com/blah/**')
    }))
}

对于Gradle6 使用类似下面的东西,因为他们制作了 classDirectories as final,我们无法重新分配它,但是存在 setter 方法 classDirectories.setFrom 可以利用

    jacocoTestReport {
    reports {
        xml.enabled true
        html.enabled true
        html.destination file("$buildDir/reports/jacoco")
    }

    afterEvaluate {
        classDirectories.setFrom(files(classDirectories.files.collect {
            fileTree(dir: it,
                    exclude: ['**/server/**',
                              '**/model/**',
                              '**/command/**'
                    ]
            )
        }))
    }
}

为了在 jacoco 报告中进行过滤,需要在两个任务 jacocoTestReportjacocoTestCoverageVerification 中进行排除。

示例代码

    def jacocoExclude = ['**/example/**', '**/*Module*.class']

    jacocoTestReport {
        afterEvaluate {
            getClassDirectories().setFrom(classDirectories.files.collect {
                fileTree(dir: it, exclude: jacocoExclude)
            })
        }
    }

    jacocoTestCoverageVerification {
        afterEvaluate {
            getClassDirectories().setFrom(classDirectories.files.collect {
                fileTree(dir: it, exclude: jacocoExclude)
            })
        }

        ...
    }



对我来说,我必须在 2 个地方进行排除

jacocoTestReport {
    dependsOn test // tests are required to run before generating the report


    afterEvaluate {
        excludedClassFilesForReport(classDirectories)
    }
}

jacocoTestCoverageVerification {

    afterEvaluate {
        excludedClassFilesForReport(classDirectories)
    }
}

private excludedClassFilesForReport(classDirectories) {
    classDirectories.setFrom(files(classDirectories.files.collect {
        fileTree(dir: it,
            exclude: [
                    'com/test/a/config/**',
                    'com/test/b/constant/**',
                    'com/test/c/MainApp.class'
            ]
    )
}))
}

Gradle 6.8.3抛出异常。 Cannot set the value of read-only property 'classDirectories' for task ':jacocoTestReport' of type org.gradle.testing.jacoco.tasks.JacocoReport.

所以我找到了解决问题的方法

classDirectories.setFrom(
            fileTree(dir: "build/classes/java/main")
                    .filter({file -> !file.path.contains('/dir1')})
                    .filter({file -> !file.path.contains('/dir2')})
                    .filter({file -> !file.path.contains('/dir3')})
    )

对于 Kotlin 用户,这是我使用的 (gradle 6.7)

build.gradle.kts

tasks.jacocoTestReport {
    // tests are required to run before generating the report
    dependsOn(tasks.test) 
    // print the report url for easier access
    doLast {
        println("file://${project.rootDir}/build/reports/jacoco/test/html/index.html")
    }
    classDirectories.setFrom(
        files(classDirectories.files.map {
            fileTree(it) {
                exclude("**/generated/**", "**/other-excluded/**")
            }
        })
    )
}

此处建议:https://github.com/gradle/kotlin-dsl-samples/issues/1176#issuecomment-610643709

对于在 Kotlin DSL 中寻找这个答案的任何人,这里是:

val jacocoExclude = listOf("**/entities/**", "**/dtos/**")
jacocoTestReport {
    reports {
        xml.isEnabled = true
        csv.isEnabled = false
    }
    classDirectories.setFrom(classDirectories.files.map {
        fileTree(it).matching {
            exclude(jacocoExclude)
        }
        })
}
test {
    useJUnitPlatform()
    systemProperty("gradle.build.dir", project.buildDir)
    finalizedBy(jacocoTestReport)
    extensions.configure(JacocoTaskExtension::class) {
        excludes = jacocoExclude
    }
}

对于那些仍在挠头从覆盖率报告中过滤某些包的人来说,这里是使用最新库的适合我的配置。

   Build tool: Gradle 6.5 (also tried for 6.7)
   Coverage Tool: Jacoco 0.8.5

事情要consider/Justifications

  • afterScript 不需要
  • 需要排除两次,一次用于报告生成和覆盖验证
  • intellij IDE 建议使用 excludes 参数而不是 exclude。两者都可以正常工作
  • 在提供正则表达式时,请务必提供 .class 文件而不是 .java 文件。
  • Post Gradle 5、classDirectories为只读字段,因此,使用classDirectories.setFrom
jacocoTestReport {
    doLast {
        println("See report file://${project.rootDir}/build/reports/jacoco/test/html/index.html")
    }
    excludedClassFilesForReport(classDirectories)
}

jacocoTestCoverageVerification {
    excludedClassFilesForReport(classDirectories)
    violationRules {
        rule {
            limit {
                minimum = 0.55
            }
        }
    }
}

private excludedClassFilesForReport(classDirectories) {
    classDirectories.setFrom(files(classDirectories.files.collect {
        fileTree(dir: it, exclude: [
                '**/common/apigateway/*.class',a
                '**/common/*/client/*Client*.class',
                '**/configuration/*ClientConfiguration.class',
                '**/model/search/*SearchService.class'
        ])
    }))
}

check.dependsOn jacocoTestCoverageVerification

这是我在 Gradle 中使用 Jacoco 0.8.5 的工作配置:

def jacocoExclusions = [
        '**/config/*Config.class',
        '**/config/*Advice.class',
        '**/security/*AuthorityRoles.class',
        '**/*Application.class'
];

jacocoTestReport {
  reports {
    xml.enabled false
    csv.enabled false
    html.destination file("${buildDir}/reports/jacocoHtml")
  }
  afterEvaluate {
    classDirectories.setFrom(files(classDirectories.files.collect {
      fileTree(dir: it,
              exclude: jacocoExclusions
      )
    }))
  }
}

jacocoTestCoverageVerification {
  afterEvaluate {
    classDirectories.setFrom(files(classDirectories.files.collect {
      fileTree(dir: it,
              exclude: jacocoExclusions
      )
    }))
  }
  violationRules {
    rule {
      excludes = jacocoExclusions
      limit {
        minimum = 0.90
      }
    }
  }
}