Android Studio 中的 Jacoco 代码覆盖率

Posted

技术标签:

【中文标题】Android Studio 中的 Jacoco 代码覆盖率【英文标题】:Jacoco code coverage in Android Studio with flavors 【发布时间】:2015-03-21 01:01:28 【问题描述】:

一段时间以来,我一直在尝试安静地运行 Jacoco 测试覆盖。我已经尝试了这些主题中报告的几种可能的解决方案:

android test code coverage with JaCoCo Gradle plugin

How do I get a jacoco coverage report using Android gradle plugin 0.10.0 or higher?

我正在使用 genymotion 在模拟设备中运行测试。 这是我添加到 build.gradle 的内容:

apply plugin: 'jacoco'

android       
    jacoco 
        version "0.7.1.201405082137"
            
    buildTypes
        debug
                    testCoverageEnabled = true
        
    


jacoco 
    toolVersion "0.7.1.201405082137"

要运行它,我使用类似的东西

./gradlew clean
./gradlew createFLAVOR_NAMEDebugCoverageReport

相关生成的文件/文件夹是:

/build/intermediates/coverage-instrumented-classes
/build/intermediates/jacoco
/build/outputs/code-coverage/connected/flavors/MyFlavor/coverage.ec

但是,@build/reports/jacoco/test/html/index.html 或任何 html 页面/代码覆盖率报告 @/build/outputs 都没有。

我还尝试创建一个专门的任务来构建覆盖率报告:

def coverageSourceDirs = [
    'src/main/java',
]

task jacocoTestReport(type: JacocoReport, dependsOn: "connectedAndroidTestFLAVOR_NAMEDebug") 
    group = "Reporting"
    description = "Generate Jacoco coverage reports after running tests."
    reports 
        xml.enabled = true
        html.enabled = true
    
    classDirectories = fileTree(
        dir: './build/intermediates/classes/debug',
        excludes: ['**/R*.class',
                   '**/*$InjectAdapter.class',
                   '**/*$ModuleAdapter.class',
                   '**/*$ViewInjector*.class'
        ])
    sourceDirectories = files(coverageSourceDirs)
    executionData = files("$buildDir/jacoco/connectedAndroidTestMyFlavorDebug.exec")
    // Bit hacky but fixes https://code.google.com/p/android/issues/detail?id=69174.
    // We iterate through the compiled .class tree and rename $$ to $.
    doFirst 
       new File("$buildDir/intermediates/classes/").eachFileRecurse  file ->
            if (file.name.contains('$$')) 
                file.renameTo(file.path.replace('$$', '$'))
            
        
    

然后是 ./gradlew clean./gradlew jacocoTestReport。输出与上面相同,因此没有包含覆盖率报告或任何其他覆盖率文件的 html 页面。

我目前正在使用最新 gradle 版本的 Android Studio v1.0.2。 我对 gradle 很陌生,所以我可能在这里遗漏了一些基本的东西。

谢谢

【问题讨论】:

github.com/bumptech/glide/blob/master/library/build.gradle 使用您的工具进行采样。 How to get code coverage using Android Studio? 的可能副本 【参考方案1】:

花了一整天的时间追查这个问题后,我发现了问题所在。与我看到的示例相反,testDebug 构建生成的文件不是 .exec 文件@$buildDir/jacoco/testDebug.exec

使用我的 gradle 和 studio 版本,生成的文件是 .ec @build/outputs/code-coverage/connected/flavors/myFlavor/coverage.ec

我没有找到任何与此相关的信息。然而,这可能是最近的变化,通过创建自定义 JacocoReport 任务并相应地更改 executionData 变量,我已经解决了这个问题。 这是我的实现:

task jacocoTestReport(type: JacocoReport) 

  def coverageSourceDirs = [
        'src/main/java'
  ]

  group = "Reporting"
  description = "Generates Jacoco coverage reports"
  reports 
      xml
          enabled = true
          destination "$buildDir/reports/jacoco/jacoco.xml"
      
      csv.enabled false
      html
          enabled true
          destination "$buildDir/jacocoHtml"
      
  

  classDirectories = fileTree(
          dir: 'build/intermediates/classes',
          excludes: ['**/R.class',
                     '**/R$*.class',
                     '**/BuildConfig.*',
                     '**/Manifest*.*',
                     '**/*Activity*.*',
                     '**/*Fragment*.*'
          ]
  )

  sourceDirectories = files(coverageSourceDirs)
  additionalSourceDirs = files(coverageSourceDirs)
  executionData = files('build/outputs/code-coverage/connected/flavors/smartcompanion/coverage.ec')

【讨论】:

通过所有这些提示,尽管我通过测试活动运行,但测试报告中的代码覆盖率仍然为 0%。但我的报告位于$buildDir/reports/coverage/debug,而不是$buildDir/reports/jacoco/jacoco.xml$buildDir/jacocoHtml。我已经花了很多时间在我的 Instrumentation Tests 中集成 Jacoco 代码覆盖率,并且在帖子中使用了很多描述。但它仍然不能正确运行。奇怪的是我的coverage.ec文件是空的。 这个 sn-p 是否应该排除所有片段和活动?那里可能有需要测试的代码。 @unlimited101 您使用哪种设备?如果使用三星可能会出现问题。尝试使用 HTC、摩托罗拉或其他一个【参考方案2】:

使用带有 Android 风格的 Jacoco 测试覆盖率报告:

假设您有名为“免费”和“付费”的口味

    在 build.gradle 所在的项目模块目录(默认应用程序)中创建文件 jacoco.gradle,它应该在 build.gradle 文件旁边。目录结构如下图

    >app > jacoco.gradle
    

    将下面的代码粘贴到我们在步骤 1 中创建的文件中,该代码具有自我解释的 cmets 以便理解

apply plugin: 'jacoco'

jacoco 
    toolVersion = "0.7.5.201505241946"

project.afterEvaluate 
    // Grab all build types and product flavors
    def buildTypes = android.buildTypes.collect  type ->
        type.name
    
    def productFlavors = android.productFlavors.collect  flavor ->
        flavor.name
    
    // When no product flavors defined, use empty
    if (!productFlavors) productFlavors.add('')

    //iterate over the flavors

    productFlavors.each 

        productFlavorName ->
//iterate over build types like debug,release,prod etc.
        buildTypes.each 

            buildTypeName ->
                //sourceName — e.g. freeDebug ,sourcePath — e.g. free/debug
            def sourceName, sourcePath
            if (!productFlavorName) 
                sourceName = sourcePath = "$buildTypeName"
             else 
                sourceName = "$productFlavorName$buildTypeName.capitalize()"
                sourcePath = "$productFlavorName/$buildTypeName"
            
                // testTaskName —  e.g. testFreeDebugtest task that the coverage task depends on,
            def testTaskName = "test$sourceName.capitalize()UnitTest"
            // Create coverage task of form 'testFlavorTypeCoverage' depending on 'testFlavorTypeUnitTest'
            task "$testTaskNameCoverage" (type:JacocoReport, dependsOn: "$testTaskName") 
                group = "Reporting"
                description = "Generate Jacoco coverage reports on the $sourceName.capitalize() build."
                classDirectories = fileTree(
                        dir: "$project.buildDir/intermediates/classes/$sourcePath",
                        excludes: [
                                '**/R.class',
                                '**/R$*.class',
                                '**/*$ViewInjector*.*',
                                '**/*$ViewBinder*.*',
                                '**/BuildConfig.*',
                                '**/Manifest*.*'
                        ]
                )
                def coverageSourceDirs = [
                        "src/main/java",
                        "src/$productFlavorName/java",
                        "src/$buildTypeName/java"
                ]
                additionalSourceDirs = files(coverageSourceDirs)
                sourceDirectories = files(coverageSourceDirs)
                executionData = files("$project.buildDir/jacoco/$testTaskName.exec")
                reports 
                    //enables and disable the type of file you need
                    xml.enabled = false
                    html.enabled = true
                
            
        
    

    在 android studio 终端中运行以下命令来构建应用程序

    ./gradlew clean assemble
    

    在构建成功后,运行以下命令以生成测试报告(将字符串 testFreeDebugUnitTestCoverage 更改为您的特定风格/构建类型,例如付费版本命令将是 ./gradlew testPaidDebugUnitTestCoverage

    ./gradlew testFreeDebugUnitTestCoverage
    

    终端应该会提示成功,现在进入目录

    >app > build > reports >jacoco >$testName >look for html or xml file report file
    

    现在您可以在浏览器中打开并查看 html 测试覆盖率文件

【讨论】:

197NODMB25385:app tarun$ ./gradlew testFreeDebugUnitTestCoverage bash: ./gradlew: No such file or directory @JJD

以上是关于Android Studio 中的 Jacoco 代码覆盖率的主要内容,如果未能解决你的问题,请参考以下文章

安卓代码覆盖率:android studio+ gradle+jacoco

如何从android项目中的jacoco测试覆盖率报告中排除方法

从精准化测试看ASM在Android中的强势插入-JaCoco初探

从精准化测试看ASM在Android中的强势插入-JaCoco初探

在带有 Gradle 的 Android 项目中使用 JaCoCo

带有 Gradle 错误的 Jenkins Android 作业,Jacoco 调试