使用 Gradle 过滤 JaCoCo 覆盖率报告

Posted

技术标签:

【中文标题】使用 Gradle 过滤 JaCoCo 覆盖率报告【英文标题】:Filter JaCoCo coverage reports with Gradle 【发布时间】:2015-07-05 10:30:13 【问题描述】:

问题:

我有一个jacoco 的项目,我希望能够过滤某些类和/或包。

相关文档:

我已阅读以下文档:

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

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

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

相关 *** 链接:

JaCoCo & Gradle - Filtering Options(无应答)

Exclude packages from Jacoco report using Sonarrunner and Gradle(不使用sonar)

JaCoCo - exclude JSP from report(它似乎对maven有效,我正在使用gradle)

Maven Jacoco Configuration - Exclude classes/packages from report not working(它似乎适用于maven,我正在使用gradle)

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"]
        
    

问题:

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

【问题讨论】:

第三方选项(我是 FD 的创始人):如果您将报告上传到 Codecov,您可以在功能部分中忽略任何您喜欢的文件的产品。谢谢。 @StevePeak 所以你可以使用Codecov在线过滤packages?另外,我看到了Githubandroid 支持呢,我看到了Java。我仍然应该将所有报告发送给您,然后在过滤之后过滤。 您可以根据您不想包含的任何文件的正则表达式方法进行过滤。通过 Jacoco 报告支持所有 java。只是在 Codecov 工作后进行过滤。它会记住您的过滤器并将其应用于所有未来的报告。谢谢! 我很好奇;那么官方文档中的excludes 实际做什么 呢?它几乎没用吗? 那个excludes不在覆盖任务上,而是在测试任务上。它排除了 JaCoCo 检测的文件,从而记录了覆盖率。如果您不想记录某些类的覆盖率,如果由于与另一个检测代理发生冲突而无法记录,或者因为您预先检测了类,则可以使用它。这不会从报告中排除一个类,尤其是在提到的最后一种情况下,这将是一个可怕的想法。 【参考方案1】:

感谢Yannick Welsch:

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

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

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

对于旧的 gradle 版本 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

【讨论】:

@BradPitcher 没问题!我花了一段时间才找到正确的答案。它看起来很“hackish”。我希望他们能想出更好的方法。 类似:exclude: ['**/*Test*.*']) jacocoTestReport 仅适用于 jacocojava 插件。这不适用于安卓。请在此处查看我的 android 仓库:github.com/jaredsburrows/android-gradle-java-template/blob/… classDirectories = 会导致此警告。 The JacocoReportBase.setClassDirectories(FileCollection) method has been deprecated. This is scheduled to be removed in Gradle 6.0. Use getClassDirectories().from(...)。展示与 Gradle 6.0 兼容的解决方案会很棒。 使用此配置获取Cannot set the value of read-only property 'classDirectories' for task ':jacocoTestReport' of type org.gradle.testing.jacoco.tasks.JacocoReport。使用 gradle 5.6 和 Java 8【参考方案2】:

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

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

【讨论】:

或者简单地使用 classDirectories.from (追加到列表而不是覆盖列表) 您将在exclude: 之后添加[] 以包含多个路径。 太好了,我使用 gradle 6.0.1,这个解决方案对我有用。 这行得通,有人可以解释一下到底发生了什么 我已更改接受的答案以反映此更改【参考方案3】:

对我来说,和我一起工作很好

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

【讨论】:

这似乎给类 0% 的覆盖率,而不是完全忽略它们。我正在使用 JaCoCi 0.8.5 和 Gradle 6.0 这是告诉jacoco不要干预某些课程的正确方法,其他方法只会影响报告部分。【参考方案4】:

对于 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/**'
                    ]
            )
        ))
    

【讨论】:

我正在尝试在 build.gradle 中使用下面的 sn-p 来排除我的 dto,但它不起作用。我正在使用 gradle 6.6.1 版本。让我知道是否有任何变化: 它,排除: ['/dto/' ] )) 【参考方案5】:

为了在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)
            )
        

        ...
    



【讨论】:

【参考方案6】:

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

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

【讨论】:

好的,感谢您确认我的发现是正确的!我希望它们使将来过滤变得更容易,或者简单地记录如何使用 Gradle 进行过滤。 有什么方法可以将源集排除在 jacoco 报告中吗?我想排除位于generated/java/ 而不是main/java 的所有源文件。 这不适用于干净的构建。 afterEvaluate 在 Gradle 的配置阶段运行,该阶段是在任何任务执行之前和编译任何类之前。所以类目录将为空【参考方案7】:

这已经有一段时间了,但我刚刚遇到了这个。我正在为所有需要的排除而苦苦挣扎。我发现这对我来说要简单得多。如果您遵循 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
                     ]
        )
    

【讨论】:

刚刚又看到了。这直接取自工作配置。也许您的 Gradle 和 Jacoco 版本与我的不同。对不起。【参考方案8】:

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

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

【讨论】:

嘿,您的版本抛出以下异常消息:“无法设置只读属性 'classDirectories' 的值”。通过更改 classDirectories -> classDirectories.from 修复。但不幸的是,我的包仍然存在于报告中【参考方案9】:

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

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

需要考虑的事项/理由

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

【讨论】:

【参考方案10】:

对我来说,我必须在 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'
            ]
    )
))

【讨论】:

【参考方案11】:

对于 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

【讨论】:

【参考方案12】:

对于任何想在 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
    

【讨论】:

【参考方案13】:

一些 cmets 提到了弃用警告。只需使用 getter 即可解决:

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

【讨论】:

【参考方案14】:

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

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

【讨论】:

coverageExcludeClasses 在 JaCoCo/Gradle 中似乎不是一个东西【参考方案15】:

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'))
    )

【讨论】:

【参考方案16】:

这是我在 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
      
    
  

【讨论】:

以上是关于使用 Gradle 过滤 JaCoCo 覆盖率报告的主要内容,如果未能解决你的问题,请参考以下文章

使用 Sonarrunner 和 Gradle 从 Jacoco 报告中排除包

使用Sonarrunner和Gradle从Jacoco报告中排除软件包

Jacoco覆盖率报告问题

勺子和Jacoco与gradle

Android 使用 jacoco 统计代码行覆盖率 无需安装运行

在带有 Gradle 的 Android 项目中使用 JaCoCo