Android test coverage report for multi module app

空扰寡人 提交于 2019-12-05 06:35:05
Eugen Martynov

This is what we had in our top build.gradle for generating HTML coverage reports:

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

And in every submodule:

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

Note that you don't need it if you do coverage analyse with Jenkins plugin or Sonar.

P.S. if you have any problems please check all paths manually, I might mistype something. It is really a pain to setup it

I have 3 modules named with gcm_demo, googleservices and networkcommunication so under build.gradle of each module write

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"
])
} 

Now in Project build.gradle write the following 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])

}

for execution use

gradlew clean jRR (short abbreviation)

after build successful output folder is

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

it provides the full project coverage of UI and unitTest

Instead of using getByPath You can also use variables to reach different modules the build.gradle itself e.g $buildDir will take you to current module build folder.

Secondly, $project.projectDir.parent will take to parent project. Example $project.projectDir.parent/<sub-project-name>/outputs/code-coverage/connected/coverage.ec

You can use your sub project name : gcm_demo, googleservices or networkcommunication as $project.projectDir.parent/gcm_demo/outputs/code-coverage/connected/coverage.ec

Note: make sure you use right file coverage.ec OR coverage.exec check whats being generated for you

To print all the paths, you can use the following task in the build.gradle: run gradle paths as defined as

task paths { println "Printing the current module build: $buildDir" println "Printing the module directory: $project.projectDir" println "Printing the parent module: $project.projectDir.parent" }

This will help you play with directories and file folders in the multi-module android projects. Problem for me was not able reach to the right folders and file directories from the sub-module build.gradle

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!