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('\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 代码覆盖率的更多相关文章
- Android自动化测试探索(五)代码覆盖率统计
Android 代码覆盖率统计 本周开始准备统计Android自动化用例的代码覆盖率,将最终使用的方法记录下来. 覆盖率监测的原理 覆盖率监测的原理跟iOS上的原理差不多,大致的思路参考下吧, iOS ...
- Jenkins构建Android项目持续集成之单元测试及代码覆盖率
单元测试 在软件开发中一直在推崇TDD(测试驱动开发),但是一直不能被有效的执行或者并不是真正的测试驱动开发(先开发后写单元测试),因为我们懒!而Android开发又是大多应用层面的开发,很多都是和视 ...
- Android自动化测试探索(七)代码覆盖率统计
之前在 https://www.cnblogs.com/zhouxihi/p/11453738.html 这篇写了一种统计Android覆盖率的方式 但是对于一些比较复杂或者代码结构不够规范的项目,有 ...
- 安卓代码覆盖率:android studio+ gradle+jacoco
在工程的oncreate()方法添加如下代码,目的是创建ec文件. String DEFAULT_COVERAGE_FILE_PATH = "/mnt/sdcard/coverage.ec& ...
- 测试代码覆盖率工具学习(Android Emma)
博客分类: 工具分享 eclipseeclemmaemmatestng 关于eclemma的历史和怎么安装,请参考http://www.ibm.com/developerworks/cn/ ...
- Android源码目录结构详解(转载)
转自:http://blog.csdn.net/xiangjai/article/details/9012387 在学习Android的过程中,学习写应用还好,一开始不用管太多代码,直接调用函数就可以 ...
- android源码的目录结构
android源码的目录结构 [以下网络摘抄] |-- Makefile ! l/ a5 n% S% @- `0 d# z# a$ P4 V3 o7 R|-- bionic ...
- Android 4.0 源代码结构
Android源码的第一级目录结构 Android/abi (abi相关代码.ABI:application binary interface,应用程序二进制接口) Android/bioni ...
- Android源码-学习随笔
在线代码网站1:http://grepcode.com/project/repository.grepcode.com/java/ext/com.google.android/android/ 书籍: ...
随机推荐
- python 读取consul配置
自动化通过rcp client调用远端服务接口时,都需要将远端测试服务ip.端口记录在配置文件. 但由于,服务发布或重启会导致ip.端口变动. 以下将通过python-consul 自动去读取cons ...
- 什么是SQL注入?(理解)
SQL注入攻击是黑客对数据库进行攻击的常用手段之一.一部分程序员在编写代码的时候,没有对用户输入数据的合法性进行判断,注入者可以在表单中输入一段数据库查询代码并提交,程序将提交的信息拼凑生成一个完整s ...
- ubuntu添加新用户并添加管理员权限
Ubuntu创建新用户并增加管理员权限 Family 2014-06-24 22:21:22 $是普通管员,#是系统管理员,在Ubuntu下,root用户默认是没有密码的,因此也就无法使用(据说是为 ...
- 洛谷 P4008 [NOI2003]文本编辑器 解题报告
P4008 [NOI2003]文本编辑器 题目描述 很久很久以前,\(DOS3.x\)的程序员们开始对 \(EDLIN\) 感到厌倦.于是,人们开始纷纷改用自己写的文本编辑器⋯⋯ 多年之后,出于偶然的 ...
- 块元素包裹img空隙问题
解决方法 1.img设为block2.img对齐方式vertical-align:bottom(middle/top),不是baseline就行3.父容器font-size:0或line-height ...
- Educational Codeforces Round 2 B. Queries about less or equal elements
打开题目连接 题意:给2个数组(无序的)啊a,b,判断b数组中的每一个元素大于a数组中个数. ACcode: #include <iostream> #include <vector ...
- 泛型与非泛型集合类的区别及使用例程,包括ArrayList,Hashtable,List<T>,Dictionary<Tkey,Tvalue>,SortedList<Tkey,Tvalue>,Queue<T>,Stack<T>等
泛型与非泛型集合类在C#程序中是非常重要的一个基础概念,这里列一个表来进行对比: 非泛型集合类 泛型集合类 描述 ArrayList List<T> 表示具有动态大小的对象数组 Hasht ...
- MAP的get与containskey
前提是:Map可以出现在k与v的映射中,v为null的情况, 所以containsKey出现更加必要. map.get(key)是得到的key所对应的value值. map.contains(key) ...
- Android 自定义动画 Loading
转自:http://my.oschina.net/janson2013/blog/118558 1.定义一个ImageView 定义一个ImageView是为了装载图片,其中的图片将被rotate用来 ...
- 牧场行走(LCA)
神奇传送门 好吧,这题很有意思.. 第一眼撇的时候还以为是(SPFA)呜.... 然后发现要Q次询问就想到了LCA 但是发现不是求LCA.. 于是想到了一个神奇的定律: 两点的LCA一定在u到v的最短 ...