5

我们有一个多模块应用程序。我们有3个图书馆项目和1个启动项目。多模块应用的Android测试覆盖率报告

模块1(Libraray) 模块2(Libraray)取决于模块1 单词数(Libraray)取决于模块1

启动(没有任何的源代码,它只是对所有LIB的启动)取决于模块1和模块2 。

在module1中,我们使用facade模式来访问模块2和模块3的类。由于我们需要在启动项目中编写所有测试用例,因为我们可以访问启动项目中的所有类,因此我们可以访问所有类,并且测试用例不会由于NoClassDefException而失败。

当我们在Launch项目中编写测试用例时,我们能够运行测试用例,我们得到的执行报告为100%,它创建了一个包含测试用例所有细节的index.html文件,但是当我尝试生成覆盖率报告,然后不显示覆盖率报告的任何数据。以下是我的gradle文件。

apply plugin: 'com.android.application' 
apply plugin: 'jacoco' 
android { 
compileSdkVersion 22 
buildToolsVersion "23.0.2"` 

defaultConfig { 
    applicationId "com.test.mobile" 
    minSdkVersion 14 
    targetSdkVersion 17 
    multiDexEnabled true 
    testApplicationId "com.test.mobile.test" 
    testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner' 
} 

repositories { 
    mavenCentral() 
} 

buildTypes { 

    release { 
     minifyEnabled true 
     proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt' 
    } 

    debug{ 
     testCoverageEnabled true 
    } 
} 


dexOptions { 
    preDexLibraries = false 
    javaMaxHeapSize "4096M" 
    jumboMode = true 
    incremental false 
} 

afterEvaluate { 
    tasks.matching { 
     it.name.startsWith('dex') 
    }.each { dx -> 
     if (dx.additionalParameters == null) { 
      dx.additionalParameters = [] 
     } 
     dx.additionalParameters += '--multi-dex' 
     dx.additionalParameters += "--main-dex-list=$projectDir\\multidex-main-dex-list.txt".toString() 
    } 
}} 
dependencies { 
compile project(':module2') 
compile project(':module3') 
compile "com.android.support.test.espresso:espresso-idling-resource:2.2.1" 

// Dependencies for local unit tests 
testCompile "junit:junit:4.12" exclude group: 'com.android.support', module: 'support-annotations' 

testCompile "org.mockito:mockito-all:1.10.19" exclude group: 'com.android.support', module: 'support-annotations' 
testCompile "org.hamcrest:hamcrest-all:1.3" exclude group: 'com.android.support', module: 'support-annotations' 
testCompile "org.powermock:powermock-module-junit4:1.6.2" exclude group: 'com.android.support', module: 'support-annotations' 
testCompile "org.powermock:powermock-api-mockito:1.6.2" exclude group: 'com.android.support', module: 'support-annotations' 


// Android Testing Support Library's runner and rules 
androidTestCompile "com.android.support.test:runner:0.4.1" exclude group: 'com.android.support', module: 'support-annotations' 
androidTestCompile "com.android.support.test:rules:0.4.1" exclude group: 'com.android.support', module: 'support-annotations' 

// Espresso UI Testing dependencies. 
androidTestCompile "com.android.support.test.espresso:espresso-core:2.2.1" exclude group: 'com.google.code.findbugs' exclude group: 'javax.annotation' exclude group: 'com.android.support', module: 'support-annotations' exclude module: 'javax.annotation-api' 
androidTestCompile "com.android.support.test.espresso:espresso-contrib:2.2.1" exclude group: 'com.google.code.findbugs' exclude group: 'javax.annotation' exclude group: 'com.android.support', module: 'support-annotations' exclude module: 'javax.annotation-api' exclude group: 'com.android.support', module: 'support-v4' 
androidTestCompile "com.android.support.test.espresso:espresso-intents:2.2.1" exclude group: 'com.google.code.findbugs' exclude group: 'javax.annotation' exclude group: 'com.android.support', module: 'support-annotations' exclude module: 'javax.annotation-api'} 

task jacocoTestReport(type: JacocoReport, dependsOn: 'testDebugUnitTest') { def projects = new ArrayList() subprojects.each { prj -> projects.add(prj) }

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

jacocoClasspath = configurations['androidJacocoAnt'] 

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" 

sourceDirectories = files([mainSrc]) 
classDirectories = files([debugTree]) 
/*sourceDirectories = generateSourceFiles(projects) 
classDirectories = generateClassDirs(projects)*/ 

executionData = files(["${buildDir}/jacoco/testDebugUnitTest.exec", 
         "${buildDir}/outputs/code-coverage/connected/coverage.ec" 
])} 
+0

你在'build.gradle'图书馆类似的变化? –

+0

其他下面是lib项目build.gradle中的一个。 'android {{{0} {编译斯德哥尔摩22 buildToolsVersion“23.0。2" defaultConfig { 的minSdkVersion 14 targetSdkVersion 17 multiDexEnabled真 } buildTypes { 释放{ minifyEnabled假 proguardFiles getDefaultProguardFile( 'proguard的-android.txt'), 'proguard的-project.txt' } } }' 测试用例信息在lib项目中不可用 –

回答

3

这是我们在我们的首要build.gradle生成HTML覆盖报告:

def coverageSourceDirs = ['app/src/main/java', 'core/src/main/java', 'database/src/main/java'] 

def coverageExcludes = ['**/R.class', 
         '**/R$*.class', 
         '**/*$$ViewBinder*.*', 
         '**/inject/*', 
         '**/*$InjectAdapter.*', 
         '**/BuildConfig.*', 
         '**/Manifest*.*', 
         '**/Dagger*.*', 
         '**/*_Provide*Factory.*', 
         '**/*_Member*Injector.*', 
         '**/*_Factory.*'] 

def coverageClassDirectories = [fileTree(dir: 'app/build/intermediates/classes/debug', excludes: coverageExcludes), 
           fileTree(dir: 'core/build/intermediates/classes/debug', excludes: coverageExcludes), 
           fileTree(dir: 'database/build/intermediates/classes/debug', excludes: coverageExcludes)] 

task jacocoRootReport(type: JacocoReport) { 
    dependsOn "app:jacocoTestReport", 
      "core:jacocoTestReport", 
      "database:jacocoTestReport" 

    additionalSourceDirs = files(coverageSourceDirs) 
    sourceDirectories = files(coverageSourceDirs) 
    classDirectories = files(coverageClassDirectories) 
    executionData = files(tasks.getByPath("app:jacocoTestReport").executionData, 
      tasks.getByPath("core:jacocoTestReport").executionData, 
      tasks.getByPath("database:jacocoTestReport").executionData  
) 

    reports { 
     html.enabled = true 
     xml.enabled = false 
     csv.enabled = false 
    } 
    onlyIf = { 
     true 
    } 

    doFirst { 
     executionData = files(executionData.findAll { 
      it.exists() 
     }) 
    } 
} 

,并在每个子模块:

android { 
    buildTypes { 
     release { 
      minifyEnabled false 
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 
     } 

     debug { 
      testCoverageEnabled = true 
     } 
    } 
} 

def coverageSourceDirs = ['src/main/java'] 

task jacocoTestReport(type: JacocoReport, dependsOn: "testJenkinsUnitTest") { 
    group = "Reporting" 

    description = "Generate Jacoco coverage reports" 

    classDirectories = fileTree(dir: 'build/intermediates/classes/debug', 
      excludes: ['**/R.class', 
         '**/R$*.class', 
         '**/*$$ViewBinder*.*', 
         '**/inject/*', 
         '**/*$InjectAdapter.*', 
         '**/BuildConfig.*', 
         '**/Manifest*.*', 
         '**/Dagger*.*', 
         '**/*_Provide*Factory.*', 
         '**/*_Member*Injector.*', 
         '**/*_Factory.*', 
         '**/PagerTitleStripV22*.*']) 

    additionalSourceDirs = files(coverageSourceDirs) 
    sourceDirectories = files(coverageSourceDirs) 
    executionData = files('build/jacoco/testDebugUnitTest.exec') 

    doFirst { 
     new File("$buildDir/intermediates/classes/").eachFileRecurse { file -> 
      if (file.name.contains('$$')) { 
       file.renameTo(file.path.replace('$$', '$')) 
      } 
     } 
    } 

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

请注意,如果使用Jenkins插件或Sonar进行覆盖率分析,则不需要它。

P.S.如果您有任何问题,请手动检查所有路径,我可能会输错一些东西。这实在是一个痛苦的设置它

+0

在所有模块中只有主模块中有测试用例 在我们的例子中,我们有一个模块包含所有模块(应用程序,核心,数据库)的所有测试用例。在该模块中,我们没有任何源代码。 –

+0

我不认为测试应该是单独的模块:)我们有测试作为库的一部分。但是这应该不是问题,应该正确设置课程,来源和覆盖率的主要路径 –

+0

HI非常感谢您的支持。 我已经更新了模拟github中的问题的测试项目。 https://github.com/HariSoni87/codecoveragetest/tree/master/junittest_coverage_launch 请你看看项目,让我们知道我们要去哪里错了。我们做了很多似乎没有工作的变化。 –

4

我有3个模块,所以在每个模块的build.gradle 写

apply plugin: 'jacoco' 

task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest', 'createDebugCoverageReport']) { 
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" 

sourceDirectories = files([mainSrc]) 
classDirectories = files([debugTree]) 
executionData = fileTree(dir: "$buildDir", includes: [ 
     "jacoco/testDebugUnitTest.exec", "outputs/code-coverage/connected/*coverage.ec" 
]) 
} 

现在,在项目与gcm_demo,googleservices和networkcommunication 命名的build.gradle编写以下scrpit

apply plugin: 'jacoco' 
task jacocoRootReport(type: JacocoReport, dependsOn: ['gcm_demo:jacocoTestReport', 'googleservice:jacocoTestReport', 'networkcommunication:jacocoTestReport']) { 
reports { 
    xml.enabled = true 
    html.enabled = true 
} 
sourceDirectories = files([tasks.getByPath("gcm_demo:jacocoTestReport").sourceDirectories, 
          tasks.getByPath("googleservice:jacocoTestReport").sourceDirectories, 
          tasks.getByPath("networkcommunication:jacocoTestReport").sourceDirectories]) 

classDirectories = files([tasks.getByPath("gcm_demo:jacocoTestReport").classDirectories, 
          tasks.getByPath("googleservice:jacocoTestReport").classDirectories, 
          tasks.getByPath("networkcommunication:jacocoTestReport").classDirectories]) 

executionData = files([tasks.getByPath("gcm_demo:jacocoTestReport").executionData, 
         tasks.getByPath("googleservice:jacocoTestReport").executionData, 
         tasks.getByPath("networkcommunication:jacocoTestReport").executionData]) 

} 

执行使用

gradlew clean jRR (short abbreviation) 

后构建成功输出文件夹是

{project location}\build\reports\jacoco\jacocoRootReport\html\index.html 

它提供了用户界面和单元测试的完整的项目覆盖

+0

我仍然可以在覆盖率报告中看到我的应用程序模块包...我无法在覆盖率报告中找到子模块包。请您详细说明如何做到这一点? 此外,我可以让我的子模块jacoc报告独立运行,并获得它的覆盖报告。然而,当我在主模块中运行jacocoreporttask时,它不包含其中的子模块包? –