【问题标题】:Android test code coverage with JaCoCo Gradle plugin使用 JaCoCo Gradle 插件的 Android 测试代码覆盖率
【发布时间】:2013-08-21 13:06:51
【问题描述】:

我是 Gradle 和 Android 测试的新手,但我已经将我的 Android 项目转换为使用 Gradle 构建。

现在我正在尝试使用 Gradle 的 JaCoCo 插件对 Android 项目进行测试覆盖。

我已将以下内容添加到我的 build.gradle 文件中:

apply plugin: 'jacoco'

当我运行“gradle jacocoTestReport”时出现以下错误:

Task 'jacocoTestReport' not found in root project '<project name>'.

从文档中我也应该应用插件“java”,但它与插件“android”冲突。

有没有办法解决这个问题?

提前致谢。

【问题讨论】:

  • android gradle 插件还不支持测试覆盖率。我也在寻找一种方法来实现,但现在看起来没有希望了,因为 android 的 gradle 插件并没有告诉 android 生成任何覆盖。
  • 在java插件和android插件兼容之前,你可以使用ant.java来执行测试并生成覆盖率报告。基本上做你在 ANT 中会做的事情。
  • @skipy:你有一个例子说明如何在 ant 中做到这一点吗?我一直找不到配置jacocoagent 并从模拟器中检索报告的示例。

标签: android testing gradle code-coverage jacoco


【解决方案1】:

这是我使用Jacoco的方式:

buildscript {
  repositories {
    mavenLocal()
    mavenCentral()
  }
  dependencies {
    classpath 'com.android.tools.build:gradle:0.12.+'
    classpath 'org.robolectric:robolectric-gradle-plugin:0.11.+'
  }
}

apply plugin: 'com.android.application'
apply plugin: 'robolectric'
apply plugin: 'jacoco'

android {
  compileSdkVersion 20
  buildToolsVersion "20.0.0"

  defaultConfig {
    applicationId "YOUR_PACKAGE_NAME"
    minSdkVersion 10
    targetSdkVersion 20
    testHandleProfiling true
    testFunctionalTest true
  }
  buildTypes {
    debug {
      testCoverageEnabled false
    }
    release {
      runProguard false
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
    }
  }
  jacoco {
    version "0.7.1.201405082137"
  }
  packagingOptions {
    exclude 'META-INF/DEPENDENCIES.txt'
    exclude 'META-INF/LICENSE.txt'
    exclude 'META-INF/NOTICE.txt'
    exclude 'META-INF/NOTICE'
    exclude 'META-INF/LICENSE'
    exclude 'META-INF/DEPENDENCIES'
    exclude 'META-INF/notice.txt'
    exclude 'META-INF/license.txt'
    exclude 'META-INF/dependencies.txt'
    exclude 'META-INF/LGPL2.1'
    exclude 'META-INF/services/javax.annotation.processing.Processor'
    exclude 'LICENSE.txt'
  }
}

robolectric {
  include '**/*Test.class'
  exclude '**/espresso/**/*.class'

  maxHeapSize "2048m"
}

jacoco {
  toolVersion "0.7.1.201405082137"
}

// Define coverage source.
// If you have rs/aidl etc... add them here.
def coverageSourceDirs = [
    'src/main/java',
]

task jacocoTestReport(type: JacocoReport, dependsOn: "connectedDebugAndroidTest") {
  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('$$', '$'))
      }
    }
  }
}


dependencies {
  androidTestCompile('junit:junit:4.11') {
    exclude module: 'hamcrest-core'
  }
  androidTestCompile('org.robolectric:robolectric:2.3') {
    exclude module: 'classworlds'
    exclude module: 'maven-artifact'
    exclude module: 'maven-artifact-manager'
    exclude module: 'maven-error-diagnostics'
    exclude module: 'maven-model'
    exclude module: 'maven-plugin-registry'
    exclude module: 'maven-profile'
    exclude module: 'maven-project'
    exclude module: 'maven-settings'
    exclude module: 'nekohtml'
    exclude module: 'plexus-container-default'
    exclude module: 'plexus-interpolation'
    exclude module: 'plexus-utils'
    exclude module: 'wagon-file'
    exclude module: 'wagon-http-lightweight'
    exclude module: 'wagon-http-shared'
    exclude module: 'wagon-provider-api'
    exclude group: 'com.android.support', module: 'support-v4'
  }
}

上述代码还包含https://code.google.com/p/android/issues/detail?id=69174 的解决方法。

更多详情:http://chrisjenx.com/gradle-robolectric-jacoco-dagger/

【讨论】:

  • 您是如何将上述脚本与 Android 插件集成的?你能给我链接看看你的 gradle 文件吗?
  • @Borys 假设您已经存在一个集成了 robolectric 的 build.gradle。您只需将apply plugin: 'jacoco' 放在apply plugin 部分,然后将上述代码的其余部分放在build.gradle 的末尾。然后您可以运行./gradlew testDebug jacocoTestReport。就是这样。
  • 嗯....我得到下一个错误:“无法确定任务':app:jacocoTestReport'的依赖关系”我错过了什么?
  • 我用的是classpath 'com.android.tools.build:gradle:0.12.+',你用的是什么版本?
  • 嘿,我刚刚更新了答案以包含整个build.gradle,希望对您有所帮助。
【解决方案2】:

我在一个使用 RoboGuice、Butterknife 和 Robolectric 的项目中使用 JaCoCo。我可以使用@Hieu Rocker 的解决方案来设置它,但是有一些小缺点,即在我们的项目中,我们使用了各种风味,并对这些风味进行了一些额外的测试,并为每种风味提供了额外的 Java 代码。我们还使用不同的构建类型。因此,依赖“testDebug”任务的解决方案还不够好。 这是我的解决方案: 在 app 模块的 build.gradle 中添加

apply from: '../app/jacoco.gradle'

然后在 app 模块内创建一个 jacoco.gradle 文件,内容如下:

应用插件:'jacoco' 雅可{ 工具版本“0.7.1.201405082137” } def getFlavorFromVariant(字符串变体名称){ def flavorString = variantName.replaceAll(/(.*)([A-Z].*)/) { all, flavorName, buildTypeName -> 风味名称 } 返回风味字符串; } def getBuildTypeFromVariant(字符串变体名称){ def buildTypeString = variantName.replaceAll(/(.*)([A-Z].*)/) { all, flavorName, buildTypeName -> “${buildTypeName.toLowerCase()}” } 返回构建类型字符串; } def getFullTestTaskName(字符串变体名称){ return "test${variantName.capitalize()}UnitTest"; } android.applicationVariants.all { 变体 -> def 变体名称 = 变体名称; def flavorFromVariant = getFlavorFromVariant("${variantName}"); def buildTypeFromVariant = getBuildTypeFromVariant("${variantName}"); def testTaskName = getFullTestTaskName("${variantName}") 任务(“jacoco${variantName.capitalize()}TestReport”,类型:JacocoReport,dependsOn:testTaskName){ 组=“报告” description = "在运行变体测试后生成 JaCoCo 覆盖率报告:${variantName}。" 报告{ xml.enabled = 真 html.enabled = 真 } 类目录 = 文件树( 目录:“./build/intermediates/classes/${flavorFromVariant}/${buildTypeFromVariant}”, 排除:['**/R*.class', '**/*$InjectAdapter.class', '**/*$ModuleAdapter.class', '**/*$ViewInjector*.class' ] ) logger.info("为风味配置 JaCoCo:${flavorFromVariant},构建类型:${buildTypeFromVariant},任务:${testTaskName}"); def coverageSourceDirs = [ '../app/src/main/java', “../app/src/${flavorFromVariant}/java” ] sourceDirectories = 文件(coverageSourceDirs) executionData = files("$buildDir/jacoco/${testTaskName}.exec") // 有点 hacky 但修复了 https://code.google.com/p/android/issues/detail?id=69174。 // 我们遍历编译的 .class 树并将 $$ 重命名为 $。 先做{ new File("$buildDir/intermediates/classes/").eachFileRecurse { 文件 -> if (file.name.contains('$$')) { file.renameTo(file.path.replace('$$', '$')) } } } } }

你可以像这样从命令行执行它:

.gradlew jacocoFlavor1DebugTestReport

.gradlew jacocoOtherflavorPrereleaseTestReport

在我们的项目中,我们使用约定不要在风味和构建类型名称中使用大写字母,但如果您的项目不遵循此约定,您可以简单地更改 getFlavorFromVariant(..) 和 getBuildTypeFromVariant(..) 函数

希望这对某人有所帮助

【讨论】:

  • 嗨 Piotr,我得到了无法确定任务“:app:jacocoDebugTestReport”的依赖关系。 > 在项目 ':app' 中找不到路径为 'testDebug' 的任务。知道为什么吗?
  • @AndroidGecko 我已经更新了答案。测试任务名称现在在末尾附加了“UnitTest”。希望对您有所帮助!
【解决方案3】:

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

https://stackoverflow.com/a/32572259/1711454 的答案还有更多内容。

【讨论】:

    【解决方案4】:

    您是否尝试添加以下内容:

    jacocoTestReport {
       group = "Reporting"
       description = "Generate Jacoco coverage reports after running tests."
       additionalSourceDirs = files(sourceSets.main.allJava.srcDirs)
    }
    

    然后运行./gradlew test jacocoTestReport 而不是运行./gradlew jacocoTestReport

    如果一切顺利,您应该在build/reports/jacoco/test/html/index.html 找到测试结果。

    【讨论】:

    • 试过你的解决方案,这是我得到的: > 找不到方法 jacocoTestReport() 用于项目 ':App' 上的参数 [build_5dkpq0odkgno9tsiihqqr1k86u$_run_closure4@49c96202]。
    • jacoco 插件与安卓插件不兼容。 jacoco 插件需要 Java 项目。
    • 那么,如何在Android项目中运行呢?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-22
    • 2020-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-12
    相关资源
    最近更新 更多