简体   繁体   English

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

[英]Jacoco and Unit Tests Code Coverage with android-gradle-plugin >= 1.1

I recently started integrating android-gradle-plugin 1.1.0 in one of my projects. 我最近开始在其中一个项目中集成android-gradle-plugin 1.1.0。 The project uses robolectric 2.4 to run unit tests. 该项目使用robolectric 2.4运行单元测试。

It's a multi module project with very complex dependencies (Some modules depend on other modules). 这是一个具有非常复杂的依赖关系的多模块项目(某些模块依赖于其他模块)。 Something like that: 像这样:

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

For a more cleared picture please see jacoco-example project. 有关更清晰的图片,请参见jacoco-example项目。

I tried to integrate JaCoCo to generate reports for the unit tests, but it seems to me that it runs only androidTests which are basically instrumentation tests. 我试图集成JaCoCo为单元测试生成报告,但是在我看来,它仅运行androidTests ,而基本上是仪器测试。

After some google'ing I've come across a few projects on GitHub and other articles, but they mainly are focused on previous versions of the android-gradle-plugin or are using other third party plugins like android-unit-test for example here . 经过一番谷歌搜索后,我在GitHub和其他文章上遇到了一些项目,但是它们主要集中在android-gradle-plugin版本上,或者在这里使用了其他第三方插件,例如android-unit-test

May be I've lost my ability to google. 可能是我失去了使用Google的能力。 But can somebody point me in a direction where I can find some documentations regarding the new stuff in android gradle plugin and how to run the jacoco task only for unit tests? 但是有人可以指出我可以在android gradle插件中找到有关新内容的文档以及如何仅针对单元测试运行jacoco任务的方向吗?

UPDATE 更新

Adopted the script from nenick's example : nenick的示例中采用了脚本:

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

After that the ./gradlew jacocoReport executes and generates the report, but it shows 0 (zero) test coverage, which is impossible because at least half of all classes are tested. 之后,。/ ./gradlew jacocoReport执行并生成报告,但是它显示0(零)测试覆盖率,这是不可能的,因为至少所有类中的一半都经过了测试。

UPDATE_2 UPDATE_2

Tried out this example . 试了这个例子 Adding the next task to one of my gradle build files: 将下一个任务添加到我的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
    }
}

Same issue, the reports are generated, but the code coverage is still zero. 同样的问题,报告已生成,但代码覆盖率仍为零。

UPDATE_3 UPDATE_3

It seams that the task from UPDATE_2 worked but only for the module with apply plugin: 'com.android.application' (The reports a generated correctly). 它表明UPDATE_2中的任务有效,但仅适用于带有Apply apply plugin: 'com.android.application'的模块apply plugin: 'com.android.application' (报告正确生成了)。 But for modules that are android libraries ( apply plugin: 'com.android.library' ) the reports show zero coverage, although the modules contain more tests then the application module. 但是对于属于android库的模块( apply plugin: 'com.android.library' ),报告显示的覆盖率为零,尽管这些模块比应用程序模块包含更多的测试。

UPDATE_4 UPDATE_4

Created a simple example project that demonstrates my issue. 创建了一个简单的示例项目来演示我的问题。 Currently if you run ./gradlew jacocoReport the report is generated, but no test coverage is displayed for the module projects. 当前,如果您运行./gradlew jacocoReport ,则会生成报告,但不会显示模块项目的测试覆盖率。 See this link 看到这个链接

Short note: When the tests were AndroidUnitTests (whiteout JUnit 4 and Robolectric) JaCoCo reports showed coverage for all the modules. 简短说明:当测试为AndroidUnitTests(白色JUnit 4和Robolectric)时,JaCoCo报告显示了所有模块的覆盖范围。

Any ideas? 有任何想法吗?

After the hassle I decided to create an open source Gradle plugin for that. 麻烦之后,我决定为此创建一个开源Gradle插件

Root build.gradle 根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'

Then simply execute 然后简单地执行

./gradlew jacocoTestReportDebug

It'll run the JUnit tests in Debug Mode and then give you the Jacoco output in xml and html form in the corresponding build directory. 它将在调试模式下运行JUnit测试,然后在相应的构建目录中以xml和html格式提供Jacoco输出。

It also supports flavors. 它还支持口味。 Having 2 flavors red and blue those tasks would be created 有两种口味的红色和蓝色将创建这些任务

  • jacocoTestReportRedDebug jacocoTestReportRedDebug
  • jacocoTestReportBlueDebug jacocoTestReportBlueDebug
  • jacocoTestReportRedRelease jacocoTestReportRedRelease
  • jacocoTestReportBlueRelease jacocoTestReportBlueRelease

After some additional search I've stumbled upon this project I had to make some modifications so that there solution can work for my type of project, but now the test coverage reports are generated properly. 经过一些额外的搜索后,我偶然发现了这个项目,我不得不进行一些修改,以使解决方案适用于我的项目类型,但是现在可以正确生成测试覆盖率报告。

I've pushed the adopted changes to my example github repo in case someone will have a similar problem in the future. 我已将采用的更改推送到我的示例github存储库中,以防将来有人遇到类似问题。

I setup my unit tests for gradle 1.2 using this blog post . 我使用此博客文章为gradle 1.2设置了单元测试。 Then I pieced together information I found here and elsewhere to add code coverage to independent modules instead of the whole project. 然后,我整理了在这里和其他地方找到的信息,以将代码覆盖率添加到独立模块而不是整个项目中。 In my library module build.gradle file, I added the following: 在我的库模块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"
        }
    }
}

And in my project build.gradle file, I added common excludes: 在我的项目build.gradle文件中,添加了常见的排除项:

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

Also, it looks like code coverage for unit tests may be coming built in in the future Issue 144664 另外,看起来单元标记的代码覆盖范围可能会在将来的版本144664中构建

Warning: This is a hack! 警告:这是骇客! Using your configuration above, I put together a hack to switch the android plugin between application and library depending on the build tasks chosen. 使用上面的配置,我整理了一个技巧,根据选择的构建任务在应用程序和库之间切换android插件。 This works well for me because I don't end up committing code with the application mode set. 这对我来说效果很好,因为我最终不会在设置应用程序模式的情况下提交代码。

// 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
    }
}

Let's hope the android tools team fixes this soon. 希望android工具团队尽快解决此问题。

I was finally able to see my code coverage of JUnit tests with Android Studio 1.1. 我终于能够看到使用Android Studio 1.1进行JUnit测试的代码覆盖率。

jacoco.gradle 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('$$', '$'))
            }
        }
    }
}

and then within the build.gradle file of the module (I put it between android and dependencies ): 然后在模块的build.gradle文件中(我将其放在androiddependencies之间):

apply from: '../jacoco.gradle'

Also in the defaultConfig block of android . 也在androiddefaultConfig块中。 I've added this (don't know if it is necessary, but I've got this from this blog ): 我添加了此内容(不知道是否有必要,但是我从此Blog中获得了此内容):

android {
    defaultConfig {
        testHandleProfiling true
        testFunctionalTest true
    }
}

Enjoy. 请享用。

You can try to use this Gradle plugin: https://github.com/arturdm/jacoco-android-gradle-plugin 您可以尝试使用此Gradle插件: https : //github.com/arturdm/jacoco-android-gradle-plugin

Basically, all you need to do is apply it like this: 基本上,您需要做的就是像这样应用它:

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'

As a result you should get a JacocoReport task for each variant. 结果,您应该为每个变量获得一个JacocoReport任务。 Run the command below to generate code coverage reports for all of them. 运行以下命令以生成所有代码覆盖率报告。

$ ./gradlew jacocoTestReport

I resolve issues with JaCoCo and make it work with latest gradle android plugin 1.1.3 我解决了JaCoCo的问题,并使其与最新的gradle android插件1.1.3一起使用

Project with latest gradle scripts: https://github.com/OleksandrKucherenko/meter 具有最新gradle脚本的项目: https : //github.com/OleksandrKucherenko/meter

References: 参考文献:

How to attach own implementation instead of Mocks in Android Studio Unit Tests? 如何在Android Studio单元测试中附加自己的实现而不是Mocks? https://plus.google.com/117981280628062796190/posts/8jWV22mnqUB https://plus.google.com/117981280628062796190/posts/8jWV22mnqUB

Small hint for everyone who try to use JaCoCo coverage in android builds... unexpected finding!!! 对尝试在Android构建中使用JaCoCo覆盖范围的所有人的一个小提示...意外发现!!! https://plus.google.com/117981280628062796190/posts/RreU44qmeuP https://plus.google.com/117981280628062796190/posts/RreU44qmeuP

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

I was facing exactly the same problem like you. 我正面临着像你一样的问题。 Today I did completely removed android studio, android sdk, gradle. 今天我确实完全删除了android studio,android sdk,gradle。 Then reinstall everything. 然后重新安装所有内容。 After that, I just added inside the app build.gradle. 之后,我只添加了应用程序build.gradle。

debug { testCoverageEnabled true } Then I run ./gradlew connectedChec. debug {testCoverageEnabled true}然后运行./gradlew connectedChec。 Everything is working perfectly. 一切工作正常。 Android studio default Jacoco working fine for me. Android studio默认的Jacoco对我来说工作正常。 I think it is also possible to create a jacocoTestReport Task and then create code coverage.I don't know why gradle and android studio was not working previously. 我认为也可以创建一个jacocoTestReport Task然后创建代码覆盖率。我不知道为什么gradle和android studio以前无法正常工作。

Please create an example and I can take a look. 请创建一个示例,我可以看看。 I guess it's some missing path configuration. 我想这是一些缺少的路径配置。

  • include all coverage files (*.exec) 包括所有覆盖率文件(* .exec)
  • add all your source paths (module/src/main/java) 添加所有源路径(module / src / main / java)
  • add all class paths (module/build/intermediates/classes/debug) 添加所有类路径(模块/构建/中间/类/调试)

here two examples how it could be look 这里有两个例子

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

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