背景

项目使用的是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('\n')
for line in ls:
if line.endswith('device'):
id_ = line.split('\t')[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

  

android 代码覆盖率的更多相关文章

  1. Android自动化测试探索(五)代码覆盖率统计

    Android 代码覆盖率统计 本周开始准备统计Android自动化用例的代码覆盖率,将最终使用的方法记录下来. 覆盖率监测的原理 覆盖率监测的原理跟iOS上的原理差不多,大致的思路参考下吧, iOS ...

  2. Jenkins构建Android项目持续集成之单元测试及代码覆盖率

    单元测试 在软件开发中一直在推崇TDD(测试驱动开发),但是一直不能被有效的执行或者并不是真正的测试驱动开发(先开发后写单元测试),因为我们懒!而Android开发又是大多应用层面的开发,很多都是和视 ...

  3. Android自动化测试探索(七)代码覆盖率统计

    之前在 https://www.cnblogs.com/zhouxihi/p/11453738.html 这篇写了一种统计Android覆盖率的方式 但是对于一些比较复杂或者代码结构不够规范的项目,有 ...

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

    在工程的oncreate()方法添加如下代码,目的是创建ec文件. String DEFAULT_COVERAGE_FILE_PATH = "/mnt/sdcard/coverage.ec& ...

  5. 测试代码覆盖率工具学习(Android Emma)

    博客分类: 工具分享 eclipseeclemmaemmatestng       关于eclemma的历史和怎么安装,请参考http://www.ibm.com/developerworks/cn/ ...

  6. Android源码目录结构详解(转载)

    转自:http://blog.csdn.net/xiangjai/article/details/9012387 在学习Android的过程中,学习写应用还好,一开始不用管太多代码,直接调用函数就可以 ...

  7. android源码的目录结构

    android源码的目录结构 [以下网络摘抄] |-- Makefile ! l/ a5 n% S% @- `0 d# z# a$ P4 V3 o7 R|-- bionic              ...

  8. Android 4.0 源代码结构

    Android源码的第一级目录结构   Android/abi (abi相关代码.ABI:application binary interface,应用程序二进制接口)   Android/bioni ...

  9. Android源码-学习随笔

    在线代码网站1:http://grepcode.com/project/repository.grepcode.com/java/ext/com.google.android/android/ 书籍: ...

随机推荐

  1. Android记事本05

    昨天: intentFilter 今天: URL和logcat 问题: ADK更新后无法打开布局文件.xml

  2. Android记事本开发01

    今天: 学习一下Android的基本知识,了解一下记事本开发大概需要哪些知识. 昨天: 无 遇到的问题:

  3. vue.$refs 的用法

    官网给出的解释是: 被用来给元素或子组件注册引用信息.引用信息将会注册在父组件的 $refs 对象上. 1.如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素; 2.如果用在子组件上,引用 ...

  4. MyBatis 基本演示

    主配置文件 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration P ...

  5. JAVA 基础开发环境 vscode 搭建 Windows下VSCode编译运行简单java

    JAVA 基础开发环境 vscode 搭建 来源 https://www.cnblogs.com/freewsf/p/7744728.html 对于使用 Visual Studio Code 的 Ja ...

  6. BZOJ 2453 维护队列 | 分块

    题目: http://www.lydsy.com/JudgeOnline/problem.php?id=2453 题解: 考虑维护每个位置的颜色上一次出现在哪里,计为pre[i],在询问l到r的时候, ...

  7. CORS跨域cookie传递

    服务端 Access-Control-Allow-Credentials:true Access-Control-Allow-Methods:* Access-Control-Allow-Origin ...

  8. vue-计算属性-computed

    /* 计算属性:vue中对数据进行一些组合与计算的话,可以使用computed(计算属性); //例如:获取当前日期,组合 */ // 组合变成10-1 var vm = new Vue({ el: ...

  9. 使AD域控服务器Administrator的密码永不过期方法。

    在安装完AD域后,管理员密码会42天就要更新一次,这样对测试比较不方便, 如果要让域控管理员账号密码永远不过期,就照着下面的方法执行: open a Command Prompt as the adm ...

  10. maven打包源码

    1. 打包 main 目录代码到 jar 归档文件 mvn source:jar 2. 打包 test 目录代码到 jar 归档文件 mvn source:test-jar