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時,它不包含其中的子模塊包? –