最近搞Android单元测试和集成测试,涉及到测试覆盖率工具Jacoco,网上文章很多,但都不全,把一些关键点记录一下,避免以后走弯路。

androidTest与test:

  • androidTest

需要Android SDK才能运行的测试用例,可以与UI相关,也可以与UI无关。需要在真机或虚拟机上运行。一般使用的框架有:Instrumentation、Espresso。我理解成集成测试。

  • test

test包下的测试用例运行不需要Android SDK,只需要JDK。这种一般只存放单元测试用例,框架一般为JUnit。但Android几乎无法实现MV完全解耦,所以需要用Mock的方式运行测试用例,常用框架:Mockito。

androidTest运行Jacoco

在Module gradle文件中添加以下代码:

apply plugin : 'com.android.application'
apply plugin: 'jacoco'
buildTypes {
    debug {
        testCoverageEnabled true
    }
}

设置了这两项之后,运行androidtest:

  1. 打开右侧的Gradle面板运行connectedAndroidTest
  2. Terminal控制台输入 gradlew connectedAndroidTest

会自动生成集成测试报告和测试覆盖率报告

image

单元测试运行Jacoco

对于test包下,如果使用jacoco生成覆盖率将会麻烦一点点

需要在gradle中自定义task,代码如下:

task jacocoTestReport(type: JacocoReport, dependsOn: ["testDebugUnitTest", "connectedAndroidTest"]) {
    group = "jacoco"
    description = "Generate Jacoco coverage reports"
    reports {
        xml.enabled = true
        html.enabled = true
    }

    def classDir = fileTree(
            dir: "${project.buildDir}/intermediates/classes/debug",
            excludes: ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', 'android/**/*.*'])
    def sourceDir = "${project.projectDir}/src/main/java"
    sourceDirectories = files([sourceDir])
    classDirectories = files([classDir])
    additionalSourceDirs = files(["${buildDir}/generated/source/buildConfig/debug", "${buildDir}/generated/source/r/debug"])
    executionData = fileTree(dir: project.projectDir, includes: ['**/*.exec', '**/*.ec'])
}

创建task的时候,设置dependsOn: [“testDebugUnitTest”, “connectedAndroidTest”],就会在运行jacocoTestReport之前先运行单元测试和集成测试。

添加task之后在右侧gradle面板中会发现,多了jacocoTestReport任务:

image

运行之后会在图示的文件夹下生成单测的覆盖率报告:

image

jacococ.gradle

可以把Jacoco相关的写成一个gradle文件,供其他module的build.gradle引用:

apply plugin: 'jacoco'

android {
    buildTypes {
        debug {
            testCoverageEnabled true
        }
    }
}

task jacocoTestReport(type: JacocoReport, dependsOn: ["testDebugUnitTest", "connectedAndroidTest"]) {
    group = "jacoco"
    description = "Generate Jacoco coverage reports"
    reports {
        xml.enabled = true
        html.enabled = true
    }

    def classDir = fileTree(
            dir: "${project.buildDir}/intermediates/classes/debug",
            excludes: ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', 'android/**/*.*'])
    def sourceDir = "${project.projectDir}/src/main/java"
    sourceDirectories = files([sourceDir])
    classDirectories = files([classDir])
    additionalSourceDirs = files(["${buildDir}/generated/source/buildConfig/debug", "${buildDir}/generated/source/r/debug"])
    executionData = fileTree(dir: project.projectDir, includes: ['**/*.exec', '**/*.ec'])
}

参考文章

Android单元测试——辅助工具介绍