简体   繁体   English

Android - Jacoco 没有正确生成 xml 报告

[英]Android - Jacoco does not generate xml report properly

We've integrated JaCoCo plugin to our Android project for measuring the code coverage via SonarQube. However, it could not be measured.我们已经将 JaCoCo 插件集成到我们的 Android 项目中,用于通过 SonarQube 测量代码覆盖率。但是,它无法测量。 Because SonarQube expects coverage report as an XML format.因为 SonarQube 期望覆盖率报告为 XML 格式。 We add a custom jacocoTestReport task to build.gradle(module).我们将自定义jacocoTestReport任务添加到 build.gradle(module)。 It generates an xml file but it does not contain any coverage information.它生成一个 xml 文件,但不包含任何覆盖信息。 Have you encountered such a problem?你遇到过这样的问题吗? If you had, do you have any solution?如果有,你有什么解决办法吗?

build.gradle(root) build.gradle(根)

classpath "org.jacoco:org.jacoco.core:0.8.5"

build.gradle(module) build.gradle(模块)

apply plugin: 'jacoco'




...




task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest']) {
reports {
    xml.enabled = true
    html.enabled = true
}

def fileFilter = ['**/R.class',
                  '**/R$*.class',
                  '**/BuildConfig.*',
                  '**/Manifest*.*',
                  '**/*Test*.*',
                  'android/**/*.*']
def debugTree = fileTree(dir: "${buildDir}/intermediates/classes/debug", excludes: fileFilter)
def mainSrc = "${project.projectDir}/src/main/java"

getSourceDirectories().setFrom(files([mainSrc]))
getClassDirectories().setFrom(files([debugTree]))
getExecutionData().setFrom(fileTree(dir: "$buildDir", includes: [
        "jacoco/testDebugUnitTest.exec",
        "outputs/code-coverage/connected/*coverage.ec"
]))

Output: Output:

在此处输入图像描述

在此处输入图像描述

Baseline: AGP 7.1.2 +, jacoco 0.88, hilt 2.42, many you have to update the jacoco.gradle file. Baseline: AGP 7.1.2 +, jacoco 0.88, hilt 2.42,很多你要更新jacoco.gradle文件。

 apply plugin: 'jacoco'
    jacoco {
    toolVersion = '0.8.7'
    }

    task jacocoTestReport { self ->
    build.dependsOn self
    }

    //Use this method to generate the HTML and XML coverage report files for 
    Unit and UI test cases
    android.testVariants.all {
    def variant = it.testedVariant
    def variantName = variant.name.capitalize()
    def unitTestTask = "test${variantName}UnitTest"
    def androidTestCoverageTask = "create${variantName}CoverageReport"
    tasks.create(name: "jacoco${name}TestReport", type: JacocoReport)

            { self ->
                group = 'Reporting'
                description = "Generates Jacoco coverage reports on the 
    ${variant.name} variant"

                reports {
                    xml.enabled(true)
                    html.enabled(true)
                }

                def excludes = [
                        // data binding
                        'android/databinding/**/*.class',
                        '**/android/databinding/*Binding.class',
                        '**/android/databinding/*',
                        '**/androidx/databinding/*',
                        '**/BR.*',
                        // android
                        '**/R.class',
                        '**/R$*.class',
                        '**/BuildConfig.*',
                        '**/Manifest*.*',
                        '**/*Test*.*',
                        'android/**/*.*',
                        // butterKnife
                        '**/*$ViewInjector*.*',
                        '**/*$ViewBinder*.*',
                        // dagger
                        '**/*_MembersInjector.class',
                        '**/Dagger*Component.class',
                        '**/Dagger*Component$Builder.class',
                        '**/*Module_*Factory.class',
                        '**/di/module/*',
                        '**/*_Factory*.*',
                        '**/*Module*.*',
                        '**/*Dagger*.*',
                        '**/*Hilt*.*',
                        // kotlin
                        '**/*MapperImpl*.*',
                        '**/*$ViewInjector*.*',
                        '**/*$ViewBinder*.*',
                        '**/BuildConfig.*',
                        '**/*Component*.*',
                        '**/*BR*.*',
                        '**/Manifest*.*',
                        '**/*$Lambda$*.*',
                        '**/*Companion*.*',
                        '**/*Module*.*',
                        '**/*Dagger*.*',
                        '**/*Hilt*.*',
                        '**/*MembersInjector*.*',
                        '**/*_MembersInjector.class',
                        '**/*_Factory*.*',
                        '**/*_Provide*Factory*.*',
                        '**/*Extensions*.*',
                        // sealed and data classes
                        '**/*$Result.*',
                        '**/*$Result$*.*'
                ]

                def javaClasses = fileTree(dir: 
    variant.javaCompileProvider.get().destinationDir,
                        excludes: excludes)
                def kotlinClasses = fileTree(dir: "${buildDir}/tmp/kotlin- 
   classes/${variant.name}",
                        excludes: excludes)
                classDirectories.setFrom(files([
                        javaClasses,
                        kotlinClasses
                ]))
                def variantSourceSets = "${project.projectDir}/src/main/java"
                sourceDirectories.setFrom(project.files(variantSourceSets))
                def androidTestsData = fileTree(dir: 
    "${buildDir}/outputs/code_coverage/${variant.name}AndroidTest/connected/", 
    includes: ["**/*.ec"])
                executionData(files([                        
"$project.buildDir/outputs/unit_test_code_coverage/${variant.name}UnitTest/test 
   ${variantName}UnitTest.exec",
                        androidTestsData
                ]))

                dependsOn unitTestTask, androidTestCoverageTask
                jacocoTestReport.dependsOn self
            }
      }

Due to AGP being updated below path is changed in the build directory.由于 AGP 正在更新,构建目录中的路径已更改。

//Replace this code //替换这段代码

executionData.from = fileTree(dir: buildDir, includes: [
                        "jacoco/test${variantName}UnitTest.exec",
                        "/outputs/code_coverage/${variant.name}AndroidTest/connected/*coverage.ec"
                ])

//To this code //到这段代码

 def androidTestsData = fileTree(dir: "${buildDir}/outputs/code_coverage/${variant.name}AndroidTest/connected/", includes: ["**/*.ec"])
            executionData(files([
                    "$project.buildDir/outputs/unit_test_code_coverage/${variant.name}UnitTest/test${variantName}UnitTest.exec",
                    androidTestsData
            ]))

What is your gradle version?你的 gradle 版本是多少? It seems there are some issues with gradle 6.4+ https://github.com/gradle/gradle/issues/14132 gradle 6.4+ Z 5E056C500A1C4B6A7110B50D807BADE5Z 似乎存在一些问题://github.com/gradle/gradle/issues/14132

After some googling, I've finally solved this problem.经过一番谷歌搜索,我终于解决了这个问题。 Source and class directories' paths were incorrect.源和 class 目录的路径不正确。 Correct paths should be like this:正确的路径应该是这样的:

            def javaClasses = fileTree(dir: variant.javaCompileProvider.get().destinationDir,
                    excludes: fileFilter)
            def kotlinClasses = fileTree(dir: "${buildDir}/tmp/kotlin-classes/${variantName}",
                    excludes: fileFilter)

            classDirectories.setFrom(files([
                    javaClasses,
                    kotlinClasses
            ]))

            def variantSourceSets = variant.sourceSets.java.srcDirs.collect { it.path }.flatten()
            sourceDirectories.setFrom(project.files(variantSourceSets))

Regards,问候,

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM