Android library 单元测试 统计覆盖率

背景:在每个模块library中的test文件夹下写的Junit单元测试不能统计覆盖率。必须将'com.android.library'更改为'com.android.application'方能统计此模块下的单元测试覆盖率。

为方便统计全局覆盖率,特此通过jacoco和脚本实现统计。

前期准备工作

1.全局统计jacoco-all.gradle

apply plugin: 'jacoco'
jacoco {
    toolVersion = "0.8.7" //指定jacoco的版本
    reportsDir = file("$buildDir/JacocoReport") //指定jacoco生成报告的文件夹
}

/*tasks.withType(Test) {
    //jacoco.includeNoLocationClasses = true
    jacoco.excludes = ['jdk.internal.*']
}*/

android {
    defaultPublishConfig "debug"
    buildTypes {
        debug {
            //打开覆盖率统计开关
            testCoverageEnabled = true
        }
    }
    testOptions {
        unitTests.all {
            jacoco {
                includeNoLocationClasses = true
                jacoco.excludes = ['jdk.internal.*']
            }
        }
        unitTests {
            includeAndroidResources = true
        }
    }
}

task jacocoTestReport(type: JacocoReport,dependsOn: ["testDebugUnitTest","createDebugCoverageReport"]) {
    group = "Reporting"

    reports {
        xml.enabled = true
        html.enabled = true
    }
    def fileFilter = ['**/R*.class',
                      '**/*$InjectAdapter.class',
                      '**/*$ModuleAdapter.class',
                      '**/*$ViewInjector*.class',
                      '**/*Binding*.class',
                      '**/*BR*.class'
    ]
    def classDirectoriesFiles = files()

    project.rootProject.allprojects.each { project ->
        def debugTree = fileTree(
                dir: "${project.projectDir}/build/intermediates/javac/debug/classes",
                excludes: fileFilter)
        def kotlinDebugTree = fileTree(
                dir: "${project.projectDir}/build/tmp/kotlin-classes/debug")
        classDirectoriesFiles.from.add(debugTree)
        classDirectoriesFiles.from.add(kotlinDebugTree)
    }
    classDirectories.from = classDirectoriesFiles

    def executionDataFiles = files()
    project.rootProject.allprojects.each { project ->
        def executionDataFile = file("${project.projectDir}/build/jacoco/testDebugUnitTest.exec")
        if (executionDataFile.exists() || project.name == "app"){
            executionDataFiles.from.add(executionDataFile)
        }
    }
    executionData.from = executionDataFiles//指定报告数据的路径
}

2.单个Library统计jacoco.gradle

apply plugin: 'jacoco'
jacoco {
    toolVersion = "0.8.7" //指定jacoco的版本
    reportsDir = file("$buildDir/JacocoReport") //指定jacoco生成报告的文件夹
}

/*tasks.withType(Test) {
    //jacoco.includeNoLocationClasses = true
    jacoco.excludes = ['jdk.internal.*']
}*/

android {
    defaultPublishConfig "debug"
    buildTypes {
        debug {
            //打开覆盖率统计开关
            testCoverageEnabled = true
        }
    }
    testOptions {
        unitTests.all {
            jacoco {
                includeNoLocationClasses = true
                jacoco.excludes = ['jdk.internal.*']
            }
        }
        unitTests {
            includeAndroidResources = true
        }
    }
}
task jacocoTestReport(type: JacocoReport,dependsOn: ["testDebugUnitTest"]) {
//task jacocoTestReport(type: JacocoReport,dependsOn: ["createDebugCoverageReport"]) {
    group = "Reporting"

    reports {
        xml.enabled = true
        html.enabled = true
    }
    def fileFilter = ['**/R*.class',
                      '**/*$InjectAdapter.class',
                      '**/*$ModuleAdapter.class',
                      '**/*$ViewInjector*.class',
                      '**/*Binding*.class',
                      '**/*BR*.class'
    ]

    def debugTree = fileTree(
            dir: "${buildDir}/intermediates/javac/debug/classes",
            excludes: fileFilter)
    def kotlinDebugTree = fileTree(
            dir: "${buildDir}/tmp/kotlin-classes/debug")

    classDirectories.from = files(
            debugTree, kotlinDebugTree
    )

    /*def debugTree = fileTree(
            dir: "${buildDir}/intermediates/javac/debug/classes",
            excludes: fileFilter)

    classDirectories.from = files(
            debugTree
    )*/

    /*def mainSrc = "${project.projectDir}/src/main/java" //指定源码目录
    additionalSourceDirs.from = files([mainSrc])
    sourceDirectories.from = files([mainSrc])*/
    executionData.from = files("${buildDir}/jacoco/testDebugUnitTest.exec")//指定报告数据的路径
}

3.子模块配置jacoco-config.gradle

apply plugin: 'jacoco'

android {
    defaultPublishConfig "debug"
    buildTypes {
        debug {
            minifyEnabled false
            testCoverageEnabled = true
        }

        coverageDebug {
            minifyEnabled false
            testCoverageEnabled true
        }
    }
}

4.gradle.properties 内自定义值

org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
android.useAndroidX=true
android.enableJetifier=true
kotlin.code.style=official

#此处有几个Library要写几个对应的变量,默认要写成false
isLibCommonJacocoReport=false

上述四个文件都是放在应该的根目录下的

各模块gradle内的配置示例

1.主入口app内的build.gradle配置

plugins {
    id 'com.android.application'
}
//此段一定要放在application下,否则会报错
apply from: rootProject.file('jacoco-all.gradle')

2.Library下的build.gradle配置,此处示例为lib/common目录

//根据isLibCommonJacocoReport变量值得更新将library替换成application生成当前模块的覆盖率
if (isLibCommonJacocoReport.toBoolean()){
    apply plugin: 'com.android.application'
    apply from: rootProject.file('jacoco.gradle')
}else {
    apply plugin: 'com.android.library'
    apply from: rootProject.file('jacoco-config.gradle')
}

单个模块做当前模块的覆盖率统计操作

执行命名行:gradlew lib:common:jacocoTestReport -PisLibCommonJacocoReport="true"

上面的lib:commons是library的目录,PisLibCommonJacocoReport是给isLibCommonJacocoReport赋值为true

统计所有模块的覆盖率,并合成到app下

实现的原理:将所有子Library下的单元测试覆盖率全部统计完成会生成当前模块的的testDebugUnitTest.exec文件,然后使用上述文件的所有集合最后执行app的覆盖率统计

统计脚本

@echo off

gradlew lib:common:jacocoTestReport -PisLibCommonJacocoReport="true" && gradlew app:jacocoTestReport

pause
  1. gradlew lib:common:jacocoTestReport -PisLibCommonJacocoReport="true"是统计当前library的覆盖率命令

    注:不同模块要使用对应的变量名-isLibCommonJacocoReport,命令执行路径也要对

  2. gradlew app:jacocoTestReport是将所有的testDebugUnitTest.exe集合处理

缺点:非常明显的统计时长过长

大家有什么好的处理方案欢迎给出,感谢。

你可能感兴趣的:(Android library 单元测试 统计覆盖率)