2013-08-21 252 views
32

我是Gradle和Android测试的新手,但我已经将我的Android项目转换为使用Gradle构建。JaCoCo Gradle插件的Android测试代码覆盖率

现在我试图用Gradle的JaCoCo插件来测试Android项目的测试覆盖率。

我已经添加了以下到我的build.gradle文件:

apply plugin: 'jacoco' 

当我运行 “gradle这个jacocoTestReport” 以下错误:

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

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

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

在此先感谢。

+0

测试覆盖率不是由Android的gradle这个插件被支持。我正在寻找一种实现方式,但现在看起来绝望了,因为Android的gradle插件并没有告诉android生成任何覆盖。 – Snicolas

+0

,直到java插件和android插件兼容,你可以使用ant.java来执行测试并生成覆盖率报告。基本上做你会在ANT做的事情。 – skipy

+2

@skipy:你有一个如何在蚂蚁做这个例子吗?我一直无法找到配置'jacocoagent'并从模拟器获取报告的例子。 – unholysampler

回答

22

这里是我如何使用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/

+0

你是如何将Android脚本与Android插件整合的?你能给我链接看看你的gradle文件吗? – Borys

+0

@Borys假设您已经有一个集成了robolectric的存在'build.gradle'。你只需要在'apply plugin'部分放上'apply plugin:'jacoco'',然后把上面的其他代码放到'build.gradle'的末尾。然后你可以运行'./gradlew testDebug jacocoTestReport'。而已。 –

+0

嗯....我得到一个错误:“无法确定任务的依赖关系‘:应用程序:jacocoTestReport’”我错过? – Borys

-3

你尝试添加以下内容:

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

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

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

+1

试过您的解决方案,这是我得到: >找不到参数[build_5dkpq0odkgno9tsiihqqr1k86u $ @ _run_closure4 49c96202]项目法jacocoTestReport()“:应用程序”。 – Egor

+1

jacoco插件与android插件不兼容。 jacoco插件需要Java项目。 – skyisle

+0

那么,如何在Android项目中运行它? – Borys

6

我使用JaCoCo使用RoboGuice,Butterknife和Robolectric项目。我可以使用@Hieu Rocker的解决方案进行设置,但是存在一些小缺陷,即在我们的项目中,我们使用了flavor,并且为这些flavor定义了额外的测试,并为它们中的每一个添加了额外的java代码。我们也使用不同的构建类型。因此,依靠“testDebug”任务的解决方案还不够好。 这里是我的解决方案: 在的build.gradle应用模块添加

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

然后创建一个jacoco.gradle文件应用模块具有以下内容的内部:

 

    apply plugin: 'jacoco' 

    jacoco { 
     toolVersion "0.7.1.201405082137" 
    } 

    def getFlavorFromVariant(String variantName) { 
     def flavorString = variantName.replaceAll(/(.*)([A-Z].*)/) { all, flavorName, buildTypeName -> 
      flavorName 
     } 
     return flavorString; 
    } 

    def getBuildTypeFromVariant(String variantName) { 
     def buildTypeString = variantName.replaceAll(/(.*)([A-Z].*)/) { all, flavorName, buildTypeName -> 
      "${buildTypeName.toLowerCase()}" 
     } 
     return buildTypeString; 
    } 

    def getFullTestTaskName(String variantName) { 
     return "test${variantName.capitalize()}UnitTest"; 
    } 

    android.applicationVariants.all { variant -> 
     def variantName = variant.name; 
     def flavorFromVariant = getFlavorFromVariant("${variantName}"); 
     def buildTypeFromVariant = getBuildTypeFromVariant("${variantName}"); 
     def testTaskName = getFullTestTaskName("${variantName}") 

     task ("jacoco${variantName.capitalize()}TestReport", type: JacocoReport, dependsOn: testTaskName) { 
      group = "Reporting" 
      description = "Generate JaCoCo coverage reports after running tests for variant: ${variantName}." 
      reports { 
       xml.enabled = true 
       html.enabled = true 
      } 

      classDirectories = fileTree(
        dir: "./build/intermediates/classes/${flavorFromVariant}/${buildTypeFromVariant}", 
        excludes: ['**/R*.class', 
           '**/*$InjectAdapter.class', 
           '**/*$ModuleAdapter.class', 
           '**/*$ViewInjector*.class' 
        ] 
      ) 

      logger.info("Configuring JaCoCo for flavor: ${flavorFromVariant}, buildType: ${buildTypeFromVariant}, task: ${testTaskName}"); 

      def coverageSourceDirs = [ 
        '../app/src/main/java', 
        "../app/src/${flavorFromVariant}/java" 
      ] 
      sourceDirectories = files(coverageSourceDirs) 
      executionData = files("$buildDir/jacoco/${testTaskName}.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('$$', '$')) 
        } 
       } 
      } 
     } 
    } 

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

.gradlew jacocoFlavor1DebugTestReport 

.gradlew jacocoOtherflavorPrereleaseTestReport 

在我们的项目中,我们使用一个约定不使用大写字母的味道在里面,并建立类型名称,但如果你的项目没有遵循这个惯例,你可以简单地改变getFlavorFromVariant(。 。)getBuildTypeFromVariant(..)功能

希望这可以帮助别人

+4

嗨Piotr,我得到 无法确定任务':app:jacocoDebugTestReport'的依赖关系。 >在项目':app'中找不到路径'testDebug'的任务。任何想法为什么? – AndroidGecko

+0

@AndroidGecko我已经更新了答案。测试任务名称末尾附有'UnitTest'。希望能帮助到你! –

相关问题