Jacoco和Unit使用android-gradle-plugin> = 1.1测试代码覆盖率

时间:2015-02-18 15:10:12

标签: android unit-testing android-gradle jacoco

我最近开始在我的一个项目中集成android-gradle-plugin 1.1.0。该项目使用robolectric 2.4来运行单元测试。

这是一个具有非常复杂依赖关系的多模块项目(某些模块依赖于其他模块)。这样的事情:

--> application-module (dependsOn: module1, module2, module-core)
    --> module1 (dependsOn: module-core)
    --> module2 (dependsOn: module-core)
    --> module-core (dependsOn: module3, module4)
        --> module3 (library dependencies)
        --> module4 (library dependencies)

如需更清晰的图片,请参阅jacoco-example项目。

我尝试集成JaCoCo来生成单元测试的报告,但在我看来它只运行androidTests,这基本上是仪器测试。

经过一些谷歌搜索后,我遇到了一些关于GitHub和其他文章的项目,但他们主要关注android-gradle-plugin的早期版本,或者正在使用其他第三方插件,如android-unit-test for example here

可能是我失去了谷歌的能力。但有人可以指出我可以找到一些关于android gradle插件中的新内容以及如何仅针对单元测试运行jacoco任务的文档吗?

更新

采用nenick's example的脚本:

apply plugin: "jacoco"

configurations {
    jacocoReport
}

task jacocoReport(dependsOn: 'testDebug') << {
    ant {
        taskdef(name:'jacocoreport',
                classname: 'org.jacoco.ant.ReportTask',
                classpath: configurations.jacocoReport.asPath)

        mkdir dir: "${buildDir}/test-coverage-report"
        mkdir dir: "${buildDir}/reports/jacoco/test/"

        jacocoreport {
            executiondata = files("${buildDir}/jacoco/testDebug.exec")

            structure(name: "${rootProject.name}") {
                classfiles {
                    fileset (dir: "${buildDir}/intermediates/classes/debug") {
                        //exclude(name: '**/*_*.class')
                        exclude(name: '**/R.class')
                        exclude(name: '**/R$*.class')
                        exclude(name: '**/BuildConfig.class')
                    }
                }

                sourcefiles {
                    fileset dir: "src/main/java"
                    fileset dir: "${buildDir}/generated/source/buildConfig/debug"
                    fileset dir: "${buildDir}/generated/source/r/debug"
                }
            }

            xml destfile: "${buildDir}/reports/jacoco/test/jacocoTestReport.xml"
            html destdir: "${buildDir}/test-coverage-report/"
        }
    }
}

dependencies {
    jacocoReport 'org.jacoco:org.jacoco.ant:0.7.2.201409121644'
}

之后./gradlew jacocoReport执行并生成报告,但它显示0(零)测试覆盖率,这是不可能的,因为至少有一半的类都经过测试。

UPDATE_2

试过这个example。将下一个任务添加到我的一个gradle构建文件中:

task jacocoTestReport(type:JacocoReport, dependsOn: "testDebug") {
    group = "Reporting"
    description = "Generate Jacoco coverage reports"

    classDirectories = fileTree(
            dir: "${buildDir}/intermediates/classes/debug",
            excludes: ['**/R.class',
                       '**/R$*.class',
                       '**/*$ViewInjector*.*',
                       '**/BuildConfig.*',
                       '**/Manifest*.*']
    )

    sourceDirectories = files("${buildDir.parent}/src/main/java")
    additionalSourceDirs = files([
            "${buildDir}/generated/source/buildConfig/debug",
            "${buildDir}/generated/source/r/debug"
    ])
    executionData = files("${buildDir}/jacoco/testDebug.exec")

    reports {
        xml.enabled = true
        html.enabled = true
    }
}

同样的问题,报告已生成,但代码覆盖率仍为零。

UPDATE_3

它接收UPDATE_2中的任务有效但仅适用于具有apply plugin: 'com.android.application'的模块(报告生成正确)。但是对于作为android库(apply plugin: 'com.android.library')的模块,报告显示零覆盖,尽管模块包含更多测试,然后是应用程序模块。

UPDATE_4

创建了一个演示我的问题的简单示例项目。目前,如果您运行./gradlew jacocoReport,则会生成报告,但不会显示模块项目的测试覆盖率。见link

简短说明:当测试是AndroidUnitTests(whiteout JUnit 4和Robolectric)时,JaCoCo报告显示所有模块的覆盖范围。

有什么想法吗?

9 个答案:

答案 0 :(得分:15)

麻烦之后我决定为此创建一个open source Gradle plugin

Root build.gradle

buildscript {
    repositories {
        mavenCentral() // optional if you have this one already
    }
    dependencies {
        classpath 'com.vanniktech:gradle-android-junit-jacoco-plugin:0.8.0'
    }
}

apply plugin: 'com.vanniktech.android.junit.jacoco'

然后只需执行

./gradlew jacocoTestReportDebug

它将在调试模式下运行JUnit测试,然后在相应的构建目录中以xml和html格式提供Jacoco输出。

它还支持口味。有两种颜色的红色和蓝色将创建这些任务

  • jacocoTestReportRedDebug
  • jacocoTestReportBlueDebug
  • jacocoTestReportRedRelease
  • jacocoTestReportBlueRelease

答案 1 :(得分:8)

经过一些额外的搜索,我偶然发现了project 我必须进行一些修改,以便解决方案可以适用于我的项目类型,但现在可以正确生成测试覆盖率报告。

我已将所采用的更改推送到example github repo以防将来有人遇到类似问题。

答案 2 :(得分:3)

我使用此blog post为gradle 1.2设置单元测试。然后我拼凑了我在这里和其他地方发现的信息,将代码覆盖率添加到独立模块而不是整个项目。在我的库模块build.gradle文件中,我添加了以下内容:

apply plugin: 'jacoco'

def jacocoExcludes = [
        'com/mylibrary/excludedpackage/**'
]

android {
    ...
}

android.libraryVariants.all { variant ->
    task("test${variant.name.capitalize()}WithCoverage", type: JacocoReport, dependsOn: "test${variant.name.capitalize()}") {
        group = 'verification'
        description = "Run unit test for the ${variant.name} build with Jacoco code coverage reports."

        classDirectories = fileTree(
                dir: variant.javaCompile.destinationDir,
                excludes: rootProject.ext.jacocoExcludes.plus(jacocoExcludes)
        )
        sourceDirectories = files(variant.javaCompile.source)
        executionData = files("${buildDir}/jacoco/test${variant.name.capitalize()}.exec")

        reports {
            xml.enabled true
            xml.destination "${buildDir}/reports/jacoco/${variant.name}/${variant.name}.xml"
            html.destination "${buildDir}/reports/jacoco/${variant.name}/html"
        }
    }
}

在我的项目build.gradle文件中,我添加了常见的排除项:

ext.jacocoExcludes = [
    'android/**',
    '**/*$$*',
    '**/R.class',
    '**/R$*.class',
    '**/BuildConfig.*',
    '**/Manifest*.*',
    '**/*Service.*'
]

此外,看起来单元测试的代码覆盖率可能会在未来内置Issue 144664

答案 3 :(得分:2)

警告:这是一个黑客!使用上面的配置,我整理了一个hack来根据所选的构建任务在应用程序和库之间切换android插件。这对我很有用,因为我最终不会使用应用程序模式集提交代码。

// dynamically change the android plugin to application if we are running unit tests or test reports.
project.ext.androidPlugin = 'com.android.library'
for (String taskName : project.gradle.startParameter.taskNames) {
    if (taskName.contains('UnitTest') || taskName.contains('jacocoTestReport')) {
        project.ext.androidPlugin = 'com.android.application'
        break
    }
}

logger.lifecycle("Setting android pluging to ${project.ext.androidPlugin}")
apply plugin: project.ext.androidPlugin

...

apply plugin: 'jacoco'

configurations {
    jacocoReport
}

task jacocoTestReport(type:JacocoReport, dependsOn: "testDebug") {
    group = "Reporting"
    description = "Generate Jacoco coverage reports"

    classDirectories = fileTree(
            dir: "${buildDir}/intermediates/classes/debug",
            excludes: ['**/R.class',
                       '**/R$*.class',
                       '**/*$ViewInjector*.*',
                       '**/BuildConfig.*',
                       '**/Manifest*.*']
    )

    sourceDirectories = files("${buildDir.parent}/src/main/java")
    additionalSourceDirs = files([
            "${buildDir}/generated/source/buildConfig/debug",
            "${buildDir}/generated/source/r/debug"
    ])
    executionData = files("${buildDir}/jacoco/testDebug.exec")

    reports {
        xml.enabled = true
        html.enabled = true
    }
}

让我们希望Android工具团队尽快解决这个问题。

答案 4 :(得分:2)

我终于能够看到我使用Android Studio 1.1进行JUnit测试的代码覆盖率。

<强> jacoco.gradle

apply plugin: 'jacoco'

jacoco {
    toolVersion "0.7.1.201405082137"
}

def coverageSourceDirs = [
        "$projectDir/src/main/java",
]

task jacocoTestReport(type: JacocoReport, dependsOn: "testDebug") {
    group = "Reporting"
    description = "Generate Jacoco coverage reports after running tests."
    reports {
        xml.enabled = true
        html.enabled = true
    }
    classDirectories = fileTree(
            dir: './build/intermediates/classes/debug',
            excludes: ['**/R*.class',
                       '**/*$InjectAdapter.class',
                       '**/*$ModuleAdapter.class',
                       '**/*$ViewInjector*.class'
            ]
    )
    sourceDirectories = files(coverageSourceDirs)
    executionData = files("$buildDir/jacoco/testDebug.exec")
    // Bit hacky but fixes https://code.google.com/p/android/issues/detail?id=69174.
    // We iterate through the compiled .class tree and rename $$ to $.
    doFirst {
        new File("$buildDir/intermediates/classes/").eachFileRecurse { file ->
            if (file.name.contains('$$')) {
                file.renameTo(file.path.replace('$$', '$'))
            }
        }
    }
}

然后在模块的build.gradle文件中(我把它放在androiddependencies之间):

apply from: '../jacoco.gradle'

同样位于defaultConfig的{​​{1}}区块中。我已添加此内容(不知道是否有必要,但我已经从this blog获得了此内容):

android

享受。

答案 5 :(得分:1)

我解决了JaCoCo的问题并使其与最新的gradle android插件1.1.3一起使用

使用最新gradle脚本的项目:https://github.com/OleksandrKucherenko/meter

  

参考文献:

     

如何在Android Studio单元测试中附加自己的实现而不是Mocks?   https://plus.google.com/117981280628062796190/posts/8jWV22mnqUB

     

每个尝试在Android版本中使用JaCoCo覆盖范围的人的小提示......意外发现!!!   https://plus.google.com/117981280628062796190/posts/RreU44qmeuP

     

单元测试的JaCoCo XML / HTML报告 https://plus.google.com/u/0/+OleksandrKucherenko/posts/6vNWkkLed3b

答案 6 :(得分:1)

我遇到了和你一样的问题。今天我完全删除了android studio,android sdk,gradle。然后重新安装一切。之后,我刚刚在app build.gradle中添加了内容。

debug {     testCoverageEnabled是的 } 然后我运行./gradlew connectedChec。一切都很完美。 Android工作室默认Jacoco对我来说很好。我认为也可以创建一个jacocoTestReport任务,然后创建代码覆盖。我不知道为什么gradle和android studio以前没有工作。

答案 7 :(得分:1)

您可以尝试使用此Gradle插件: https://github.com/arturdm/jacoco-android-gradle-plugin

基本上,您需要做的就是这样应用:

buildscript {
  repositories {
    jcenter()
  }
  dependencies {
    classpath 'com.dicedmelon.gradle:jacoco-android:0.1.1'
  }
}

apply plugin: 'com.android.library' // or 'com.android.application'
apply plugin: 'jacoco-android'

因此,您应该为每个变体获得JacocoReport任务。运行以下命令为所有命令生成代码覆盖率报告。

$ ./gradlew jacocoTestReport

答案 8 :(得分:0)

请创建一个示例,我可以看看。我想这是一些缺少路径的配置。

  • 包括所有承保文件(* .exec)
  • 添加所有源路径(module / src / main / java)
  • 添加所有类路径(module / build / intermediates / classes / debug)

这里有两个例子如何看待