【Android】合并library单元测试覆盖率报告

单元测试,就是对我们写的代码进行最小粒度的测试,可以测试函数的执行情况及运行时间,用于减少应用发布后可能出现的BUG及性能问题。在新版本的Android Studio中,我们新建一个工程,就会自动生成单元测试目录及自动添加测试框架的依赖。
【Android】合并library单元测试覆盖率报告_第1张图片
关于单元测试,需要依赖Junit4框架和AndroidJunit4框架(新建工程默认已依赖)。如果要进行UI自动化测试就需要依赖Espresso框架,为了能够测试常规操作无法执行的条件分支,还需要依赖Mockito框架。关于这些框架,本文不作重点介绍,推荐大家阅读这几篇文章:
Android单元测试只看这一篇就够了
Android利用Espresso进行UI自动化测试的方法详解
Mock及Mockito的使用
Android代码覆盖率工具的使用
Jacoco官网

掌握了以上文章中的内容,就算已经入门单元测试的知识了。

本文重点要讲的是如何给library工程生成单元测试覆盖率报告。
【Android】合并library单元测试覆盖率报告_第2张图片

先加上默认真机测试的配置:
moulde的build.gradle:

android {
    ...
   defaultConfig {
    ...
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
 }
    

添加以上配置,就可以在真机上进行单元测试了。

在每个module的build.gradle文件中打开测试覆盖率报告:

 buildTypes {
        debug {
            testCoverageEnabled = true
        }
      ...
  }

添加以上配置后,同步一下gradle,就会生成"createDebugCovergeReport"的task(在gradle的verification分组下面)。

以上配置完成后,在项目根目录下运行命令:
./gradlew createDebugCoverageReport

运行这个task就会自动跑完androidTest和test目录下的单元测试代码,并且会在各个module的build/outputs/code-coverage/connected目录下面生成单元测试覆盖率二进行文件(*.ec格式),这个时候还不能查看像上图中html格式的覆盖率报告。接着往下看。

如果你的工程有library工程,它会在每个library工程下生成对应的测试覆盖率报告,但是它们没有合并在一起。这样我们就需要在各个library工程中写各自的单元测试代码。这样显示很麻烦。如果所有的单元测试都写在application moudle下就方便多了。试想一下,如果要查看一个项目的单元测试覆盖率,就要一个一个查看每个module下的测试报告,而且还要自己去算整体的测试用例覆盖情况,这样多不方便?有没有办法把所有module的测试报告合在一起呢?肯定是有的。我们期望的是只在applicaton module中写单元测试,并且生成的报告可以过滤一些不想统计的module。本文就是解决这个问题。

首先在你的applicaton module的build.gradle文件中加上以下脚本:

或者单独写一个report.gradle,放在config目录下,复制粘贴以下内容(第一行不要),然后在application module的build.gradle中引入report.gradle apply from:'../config/report.gradle'

apply plugin: 'com.android.application'
apply plugin: 'jacoco'
jacoco {
    //编译如果出现could not read execution data请修改这个版本号
    toolVersion = "0.8.2"
}
task jacocoTestReport(type: JacocoReport) {
    group = "Reporting"
    description = "Generate Jacoco coverage reports" 
    sourceDirectories = files() //源码
    classDirectories = files() //class字节码
    //定义需要过滤的代码
    def fileFilter = ['**/R.class', '**/R$*.class','**/BuildConfig.*', '**/Manifest*.*','android/**/*.*']
    project.rootProject.allprojects.each { project ->
        if (project.name != "app") { //报告中去除app工程中的代码
            sourceDirectories += files("${project.projectDir}" + "/src/main/java")
            classDirectories += fileTree(dir: "${project.projectDir}" + "/build/intermediates/classes/debug", excludes: fileFilter)
        }
    }
   //build时打印路径
    sourceDirectories.files.each {
        logger.lifecycle(it.path)
    }
    classDirectories.files.each {
        logger.lifecycle(it.path)
    }
//设置ec文件源
    executionData = fileTree(dir: "$buildDir", includes: ["outputs/code-coverage/connected/*.ec"])
    reports {//报告生成目录
        xml.enabled = false
        html.enabled = true
        html.destination file("${buildDir}/reports/allReports")
    }
}

另外,推荐项目根目录下的build.gradle中配置的gradle插件(classpath那一行)版本使用3.1.1,3.2.1版本在module/build/intermediates目录下已经没有classes文件夹了,所有以上jacoco的配置是无效的。另外也不要使用1.5.0这种老的gradle插件,因为在老版本下,library工程默认打出的是release包,而我们单元测试用的是debug版的包。如果你使用1.5.0版gradle插件,你会发现你的单元测试代码编译不通过:都找不到library中的类。
所以推荐在工程根目录下的build.gradle文件中,将gradle插件改成3.1.1,如果使用其它版本没有问题也可以,因为这个版本已经验证过没有问题。

    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.1'
    }

添加配置后,在工程根目录运行命令
./gradlew createDebugCoverageReport
执行此task后就会在xxx/build/outputs/code-coverage/connected路径下生xxx.ec文件。
再执行
./gradlew jacocoTestReport
就会在xxx/build/reports/allReports目录下生成所有工程的单元测试覆盖率报告。

注意,library工程不要添加任何与单元测试有关的框架和代码。

如果发现单元覆盖率报告是0%,不用担心,请检查你的app是否有文件读写权限,如果没有请加上文件读写权限,因为单元测试是需要SD卡做中转的。简单起见,可以将target sdk设成23以下,并在Manifest文件中声明读写权限即可。

如果发现执行jacocoTestReport生成报告时报“Unable to read execution data file coverage.ec”错误:

$ gradle jacocoTestReport
报如下错
* What went wrong:
Execution failed for task ':app:jacocoTestReport'.
> Unable to read execution data file E:\app\build\outputs\code-coverage\connected\coverage.ec

可能是你的jacoco版本太高了,请将jacoco版本改成"0.7.4+"
build.gradle修改:

jacoco {
    toolVersion = "0.7.4+"
}

扩充参考:
mock静态方法:Android PowerMockito的使用

你可能感兴趣的:(Android)