Android如何使用jacoco统计代码行覆盖率

发布时间:2021-12-09 17:13:49 作者:iii
来源:亿速云 阅读:321
# Android如何使用Jacoco统计代码行覆盖率

## 前言

在Android应用开发过程中,代码覆盖率是衡量测试质量的重要指标之一。Jacoco作为Java生态中广泛使用的代码覆盖率工具,能够帮助开发者精确统计单元测试和功能测试的代码执行情况。本文将详细介绍如何在Android项目中集成Jacoco,并生成可视化的覆盖率报告。

## 一、Jacoco简介

Jacoco(Java Code Coverage)是一个开源的Java代码覆盖率库,具有以下特点:

1. 支持指令(Instructions)、分支(Branches)、行(Lines)、方法(Methods)和类(Classes)等多维度覆盖率统计
2. 可以与JUnit、AndroidTest等测试框架无缝集成
3. 提供HTML、XML、CSV等多种报告格式
4. 支持离线插桩和运行时插桩两种模式

在Android项目中,我们通常使用Gradle插件来实现Jacoco的集成。

## 二、基础环境配置

### 1. 项目级build.gradle配置

首先需要在项目的build.gradle文件中添加Jacoco插件依赖:

```groovy
buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:7.0.0'
        classpath "org.jacoco:org.jacoco.core:0.8.7"
    }
}

2. 模块级build.gradle配置

在app模块的build.gradle文件中应用jacoco插件并配置基本参数:

apply plugin: 'com.android.application'
apply plugin: 'jacoco'

android {
    compileSdkVersion 31
    
    buildTypes {
        debug {
            testCoverageEnabled true
        }
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

jacoco {
    toolVersion = "0.8.7"
    reportsDirectory = file("$buildDir/reports/jacoco")
}

三、单元测试覆盖率配置

1. 配置测试任务

在模块的build.gradle中添加以下任务配置:

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

task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest']) {
    group = "Reporting"
    description = "Generate Jacoco coverage reports"
    
    reports {
        xml.enabled = true
        html.enabled = true
        csv.enabled = false
    }
    
    def fileFilter = [
        '**/R.class',
        '**/R$*.class',
        '**/BuildConfig.*',
        '**/Manifest*.*',
        '**/*Test*.*',
        'android/**/*.*'
    ]
    
    def debugTree = fileTree(dir: "$project.buildDir/intermediates/javac/debug", excludes: fileFilter)
    def mainSrc = "$project.projectDir/src/main/java"
    
    sourceDirectories.setFrom(files([mainSrc]))
    classDirectories.setFrom(files([debugTree]))
    executionData.setFrom(fileTree(dir: project.buildDir, includes: [
        'jacoco/testDebugUnitTest.exec'
    ]))
}

2. 执行单元测试覆盖率报告

在终端运行以下命令:

./gradlew clean testDebugUnitTest jacocoTestReport

报告生成后可以在以下路径查看: - HTML报告:app/build/reports/jacoco/jacocoTestReport/html/ - XML报告:app/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml

四、功能测试覆盖率配置

1. 配置AndroidTest任务

task jacocoAndroidTestReport(type: JacocoReport, dependsOn: ['connectedDebugAndroidTest']) {
    group = "Reporting"
    description = "Generate Jacoco coverage reports for Android tests"
    
    reports {
        xml.enabled = true
        html.enabled = true
        csv.enabled = false
    }
    
    def fileFilter = [
        '**/R.class',
        '**/R$*.class',
        '**/BuildConfig.*',
        '**/Manifest*.*',
        '**/*Test*.*',
        'android/**/*.*'
    ]
    
    def debugTree = fileTree(dir: "$project.buildDir/intermediates/javac/debug", excludes: fileFilter)
    def mainSrc = "$project.projectDir/src/main/java"
    
    sourceDirectories.setFrom(files([mainSrc]))
    classDirectories.setFrom(files([debugTree]))
    executionData.setFrom(fileTree(dir: project.buildDir, includes: [
        'outputs/code_coverage/debugAndroidTest/connected/*coverage.ec'
    ]))
}

2. 执行功能测试覆盖率报告

运行以下命令:

./gradlew clean connectedDebugAndroidTest jacocoAndroidTestReport

五、合并单元测试和功能测试覆盖率

1. 创建合并任务

task jacocoFullReport(type: JacocoReport, dependsOn: ['testDebugUnitTest', 'connectedDebugAndroidTest']) {
    group = "Reporting"
    description = "Generate combined Jacoco coverage reports"
    
    reports {
        xml.enabled = true
        html.enabled = true
        csv.enabled = false
    }
    
    def fileFilter = [
        '**/R.class',
        '**/R$*.class',
        '**/BuildConfig.*',
        '**/Manifest*.*',
        '**/*Test*.*',
        'android/**/*.*'
    ]
    
    def debugTree = fileTree(dir: "$project.buildDir/intermediates/javac/debug", excludes: fileFilter)
    def mainSrc = "$project.projectDir/src/main/java"
    
    sourceDirectories.setFrom(files([mainSrc]))
    classDirectories.setFrom(files([debugTree]))
    
    def unitTestData = fileTree(dir: project.buildDir, includes: ['jacoco/testDebugUnitTest.exec'])
    def androidTestData = fileTree(dir: project.buildDir, includes: ['outputs/code_coverage/debugAndroidTest/connected/*coverage.ec'])
    
    executionData.setFrom(files([unitTestData, androidTestData]))
}

2. 执行合并报告

./gradlew clean jacocoFullReport

六、高级配置与优化

1. 排除特定类或方法

可以通过注解排除特定代码:

@Generated
public class GeneratedClass {
    // 这个类将被排除在覆盖率统计之外
}

或在build.gradle中配置排除规则:

def fileFilter = [
    '**/R.class',
    '**/R$*.class',
    '**/BuildConfig.*',
    '**/Manifest*.*',
    '**/*Test*.*',
    'android/**/*.*',
    '**/model/**',  // 排除model包
    '**/*Dto*.*'   // 排除所有DTO类
]

2. 配置覆盖率阈值

可以设置覆盖率的最低阈值,当不满足时构建失败:

jacocoTestCoverageVerification {
    violationRules {
        rule {
            limit {
                minimum = 0.8 // 80%覆盖率要求
            }
        }
        
        rule {
            element = 'CLASS'
            excludes = ['*.AutoValue_*']
            limit {
                counter = 'LINE'
                value = 'COVEREDRATIO'
                minimum = 0.7
            }
        }
    }
}

3. 多模块项目配置

对于多模块项目,需要在根build.gradle中配置统一的任务:

task jacocoRootReport(type: JacocoReport) {
    dependsOn = subprojects.test
    additionalSourceDirs.setFrom(files(subprojects.sourceSets.main.allSource.srcDirs))
    sourceDirectories.setFrom(files(subprojects.sourceSets.main.allSource.srcDirs))
    classDirectories.setFrom(files(subprojects.sourceSets.main.output))
    executionData.setFrom(files(subprojects.jacocoTestReport.executionData))
    
    reports {
        html.enabled = true
        xml.enabled = true
    }
    
    doFirst {
        executionData.setFrom(files(executionData.findAll { it.exists() }))
    }
}

七、常见问题与解决方案

1. 报告显示0%覆盖率

可能原因: - 测试没有实际执行被测代码 - 执行数据(.exec或.ec文件)路径不正确 - 类文件路径配置错误

解决方案: 1. 检查测试用例是否确实调用了被测代码 2. 确认executionData路径配置正确 3. 检查classDirectories是否包含编译后的类文件

2. 包含Lambda表达式导致的问题

在Android项目中,Lambda表达式可能导致覆盖率统计异常。解决方案:

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

3. 与Instant Run冲突

Instant Run会影响Jacoco的正常工作,建议: 1. 关闭Instant Run (File → Settings → Build,Execution,Deployment → Instant Run) 2. 执行覆盖率测试前执行clean任务

4. 多Dex项目的问题

对于使用了MultiDex的项目,需要额外配置:

jacoco {
    toolVersion = "0.8.7"
    reportsDirectory = file("$buildDir/reports/jacoco")
    applyTo = all
}

八、与CI/CD集成

1. Jenkins集成配置

在Jenkinsfile中添加覆盖率检查阶段:

stage('Code Coverage') {
    steps {
        sh './gradlew jacocoFullReport'
        jacoco(
            execPattern: '**/build/jacoco/*.exec',
            classPattern: '**/build/intermediates/javac/debug/classes',
            sourcePattern: '**/src/main/java',
            exclusionPattern: '**/R.class,**/R$*.class,**/BuildConfig.*,**/Manifest*.*,**/*Test*.*,android/**/*.*'
        )
    }
}

2. SonarQube集成

在sonar-project.properties中配置:

sonar.jacoco.reportPaths=build/jacoco/jacocoFullReport.exec
sonar.java.coveragePlugin=jacoco
sonar.language=java
sonar.sourceEncoding=UTF-8

3. GitHub Actions集成

示例配置:

name: Code Coverage

on: [push, pull_request]

jobs:
  coverage:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v2
    
    - name: Set up JDK
      uses: actions/setup-java@v1
      with:
        java-version: '11'
    
    - name: Generate coverage report
      run: ./gradlew jacocoFullReport
      
    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v1
      with:
        file: ./app/build/reports/jacoco/jacocoFullReport/jacocoFullReport.xml

九、最佳实践

  1. 合理的覆盖率目标:不要盲目追求100%覆盖率,关键业务代码应保持高覆盖率
  2. 定期检查:将覆盖率检查纳入CI流程,防止覆盖率下降
  3. 关注增量覆盖率:特别关注新增代码的覆盖率
  4. 结合其他指标:将覆盖率与代码复杂度、测试质量等指标结合分析
  5. 排除生成代码:合理配置排除规则,避免生成代码影响统计结果

结语

通过本文的详细介绍,您应该已经掌握了在Android项目中使用Jacoco进行代码覆盖率统计的完整方法。从基础配置到高级技巧,从本地测试到CI集成,Jacoco为Android应用的代码质量保障提供了强有力的支持。建议开发团队将代码覆盖率作为持续改进的重要指标,不断提升测试质量和代码健壮性。

记住,代码覆盖率只是质量评估的一个维度,高覆盖率不代表没有bug,但低覆盖率通常意味着测试不足。合理的测试策略应该结合覆盖率指标和实际业务需求,构建全面的质量保障体系。 “`

这篇文章共计约4700字,详细介绍了Android项目中使用Jacoco统计代码覆盖率的方法,包含: 1. 基础环境配置 2. 单元测试和功能测试覆盖率统计 3. 高级配置技巧 4. 常见问题解决方案 5. CI/CD集成方法 6. 最佳实践建议

文章采用Markdown格式,结构清晰,代码示例丰富,可以直接用于技术文档或博客发布。

推荐阅读:
  1. python统计单元测试代码覆盖率
  2. 统计代码测试覆盖率-Python

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

android jacoco

上一篇:Redis怎么快速扫描Scan

下一篇:go语言如何处理TCP拆包/粘包

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》