android 代码覆盖率

背景

项目使用的是small插件。一个app分为main和多个插件,为了统计插件的代码覆盖率。

1 修改插件

修改插件build.gradle

    buildTypes {
        release {
			...
        }
        debug{
          minifyEnabled false
          testCoverageEnabled = true   //打开debug版本的代码覆盖率开关
          proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

  

因为工程原因插件生成的classes文件在下次生成的时候会变动。因此要讲classes文件拷贝到其他位置暂存。

  tasks.whenTaskAdded { task ->
      if (task.name == 'assembleRelease' || task.name == 'assembleDebug') {
          task.doLast {
              println "copy classes to jacoco"
              def applicationId = android.defaultConfig.applicationId
              def artifactName  = applicationId.substring(applicationId.lastIndexOf(".") + 1, applicationId.length())
              project.copy {
                  from "build/intermediates/classes/debug"
                  into "${rootDir}/../jacoco/${artifactName}/classes/debug"
              }
          }
      }
  }

2 修改main

main作为插件的容器,我们的测试代码也在这里。所有的测试用例继承于BaseTest.

给BaseTest的finishZ增加覆盖率保存功能。

        @Override
        protected void finished(Description description) {
            super.finished(description);
            .....
            generateEcFile(true);
        }

    /**
     * 生成ec文件
     *
     * @param isNew 是否重新创建ec文件
     */
    public static void generateEcFile(boolean isNew) {

        SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd_HHmmss");
        final String DEFAULT_COVERAGE_FILE_PATH = "/sdcard/coverage/" + String.format("coverage_%s.ec", df.format(new Date()));
        LogUtils.i("生成覆盖率文件: " + DEFAULT_COVERAGE_FILE_PATH);
        OutputStream out = null;
        File mCoverageFilePath = new File(DEFAULT_COVERAGE_FILE_PATH);
        try {
            if (isNew && mCoverageFilePath.exists()) {
                LogUtils.i("JacocoUtils_generateEcFile: 清除旧的ec文件");
                mCoverageFilePath.delete();
            }
            if (!mCoverageFilePath.exists()) {
                File d = new File("/sdcard/coverage/");
                d.mkdirs();
                mCoverageFilePath.createNewFile();
            }
            out = new FileOutputStream(mCoverageFilePath.getPath(), true);

            Object agent = Class.forName("org.jacoco.agent.rt.RT")
                    .getMethod("getAgent")
                    .invoke(null);

            out.write((byte[]) agent.getClass().getMethod("getExecutionData", boolean.class)
                    .invoke(agent, false));

        } catch (Exception e) {
            LogUtils.i("generateEcFile: " + e.getMessage());
        } finally {
            if (out == null)
                return;
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

编译main,生成测试apk和debug版本的apk.

3.测试

使用测试系统进行测试

4.收集所有测试被测收集的测试数据。

    import subprocess
    import os,sys

    o = subprocess.check_output(['adb','devices'])
    ls = o.split('
')
    for line in ls:
        if line.endswith('device'):
            id_ = line.split('	')[0]
            os.makedirs('./report/'+id_)
            c1 = ['adb','-s',id_,'pull','/sdcard/coverage','./report/'+id_]
            print ' '.join( c1 )
            o1 = subprocess.check_output(c1)
            print o1

5.建立新的gradle工程,生成报告。

新建文件夹jacocReport。

进入文件夹运行gradle init。

修改build.gradle

apply plugin: 'jacoco'

buildscript {
    repositories {
        mavenLocal()
        mavenCenter()
        maven { url 'plugins' }
    }
    dependencies {
        classpath 'net.researchgate:gradle-release:2.4.1'
        classpath 'com.android.tools.build:gradle:2.2.3'
    }
}


allprojects {
    repositories {
        mavenLocal()
        mavenCenter()
        flatDir {
            dirs 'libs'
        }
    }
}

//首先先删除旧的merge结果文件
task removeOldMergeEc(type: Delete) {
    delete "${rootDir}/../jacoco/coverageMerged/mergedcoverage.ec"
}

task mergeReport(type:JacocoMerge,dependsOn:removeOldMergeEc){
    group = "Reporting"
    description = "merge jacoco report."
    destinationFile= file("${rootDir}/../jacoco/coverageMerged/mergedcoverage.ec")
    //这里的ec_dir是存储ec文件的文件夹
    FileTree tree = fileTree("$projectDir/../jacoco/report") {
        include '**/*.ec'
    }
    def cnt =0
    tree.each{
      cnt++
    }
    println "ec file conut:"+cnt
    // tree.each{f->println f}
    executionData = tree  //executionData(files)
}


["plugin1",'plugin2'].each { it1->
    task "Report$it1"(type: JacocoReport,dependsOn: [mergeReport]) {it ->
      println "I'm task $it"
      group = "Reporting"
      description = "Generate Jacoco coverage reports after running tests."
      def pluginName1 =  "$it1"
      println pluginName1

      reports {
          xml.enabled = true
          html.enabled = true
      }

      classDirectories = fileTree(
              dir: "${rootDir}/../jacoco/${pluginName1}/classes/debug",
              excludes: ['**/R*.class',
                         '**/*$InjectAdapter.class',
                         '**/*$ModuleAdapter.class',
                         '**/*$ViewInjector*.class'
              ])
      def coverageSourceDirs =[
          //不需要代码路径
      ]
      sourceDirectories = files(coverageSourceDirs)
      File f = new File("${rootDir}/../jacoco/coverageMerged/mergedcoverage.ec")
      println ""+f.exists()
      println "" + f.length()+ " bytes"
      executionData = files("${rootDir}/../jacoco/coverageMerged/mergedcoverage.ec")

      doFirst {
          //修改claess 文件
          println "${rootDir}/../jacoco/${pluginName1}/classes/debug"
          new File("${rootDir}/../jacoco/${pluginName1}/classes/debug").eachFileRecurse { file ->
              if (file.name.contains('$$')) {
                  println "modify " + file.name
                  file.renameTo(file.path.replace('$$', '$'))
              }
          }
      }
    }
}

task allReport(dependsOn: [Reportplugin1,Reportplugin2]){
  doLast{
    println "done!"
  }
}

最后上目录

.
├── main          #主apk代码的目录
├── jacoco             #数据目录-存放ec文件,classes文件
├── jacocoReport       #生成报告的工程目录 build.gradle在此
├── plugin1        #插件1
├── plugin2        #插件2

  

原文地址:https://www.cnblogs.com/guhan121/p/7763468.html