白盒测试 Android jacoco 代码覆盖率测试入门

xinxi · 2018年11月28日 · 最后由 剪烛 回复于 2018年12月12日 · 3725 次阅读
本帖已被设为精华帖!

前言

最近同事搞了一个基于jacoco统计Android代码覆盖率测试的功能,可以统计每天手工测试的代码覆盖率.抱着好奇的心态,自己也学习一下jacoco,陆陆续续搞了三天终于有点结果了.

本文介绍仅仅在源码中加入少量代码就可以完成代码覆盖率覆测试.

代码配置

build.gradle

在app目录下的build.gradle配置jacoco

apply plugin: 'jacoco'
jacoco {
toolVersion = "0.7.9"
}


dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.1.1'
compile 'org.jacoco:org.jacoco.core:0.7.9'
compile 'com.android.support.constraint:constraint-layout:+'
}

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

task jacocoTestReport(type: JacocoReport) {
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/outputs/code-coverage/connected/coverage.ec")

doFirst {
new File("$buildDir/intermediates/classes/").eachFileRecurse { file ->
if (file.name.contains('$$')) {
file.renameTo(file.path.replace('$$', '$'))
}
}
}
}

写入ec文件

自定义一个JacocoUtils类,可以根据反射拿到方法、类的执行代码,写入到.ec文件

  public static void generateEcFile(boolean isNew) {
// String DEFAULT_COVERAGE_FILE_PATH = NLog.getContext().getFilesDir().getPath().toString() + "/coverage.ec";
Log.d(TAG, "生成覆盖率文件: " + DEFAULT_COVERAGE_FILE_PATH);
OutputStream out = null;
File mCoverageFilePath = new File(DEFAULT_COVERAGE_FILE_PATH);
try {
if (isNew && mCoverageFilePath.exists()) {
Log.d(TAG, "JacocoUtils_generateEcFile: 清除旧的ec文件");
mCoverageFilePath.delete();
}
if (!mCoverageFilePath.exists()) {
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));
Log.d(TAG,"写入" + DEFAULT_COVERAGE_FILE_PATH + "完成!" );
} catch (Exception e) {
Log.e(TAG, "generateEcFile: " + e.getMessage());
Log.e(TAG,e.toString());
} finally {
if (out == null)
return;
try {
out.close();
} catch (IOException e) {
e.printStackTrace();

}
}
}

使用Application生成ec

继承Application类,重写onTrimMemory方法,系统会根据不同的内存状态来回调

系统提供的回调有:
Application.onTrimMemory()
Activity.onTrimMemory()
Fragement.OnTrimMemory()
Service.onTrimMemory()
ContentProvider.OnTrimMemory()
OnTrimMemory的参数是一个int数值,代表不同的内存状态:
TRIM_MEMORY_COMPLETE:内存不足,并且该进程在后台进程列表最后一个,马上就要被清理
TRIM_MEMORY_MODERATE:内存不足,并且该进程在后台进程列表的中部。
TRIM_MEMORY_BACKGROUND:内存不足,并且该进程是后台进程。
TRIM_MEMORY_UI_HIDDEN:内存不足,并且该进程的UI已经不可见了。

可以根据level == TRIM_MEMORY_UI_HIDDEN来确定app已经至于后台,此时调用generateEcFile方法.

//判断是否是后台
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
if (level == TRIM_MEMORY_UI_HIDDEN) {
isBackground = true;
notifyBackground();
}
}

private void notifyBackground() {
// This is where you can notify listeners, handle session tracking, etc
Log.d(TAG, "切到后台");
JacocoUtils.generateEcFile(true);
}

操作步骤

给予app读写sdcard权限

因为我的是简单的demo代码,启动没有弹窗询问读写sdcard权限,
Android6.0以后是动态获取权限了,所以需要手动去设置中把sdcard权限打开,实际项目应该不存在手动打开的步骤.

手工执行

安装app->操作app->app至于后台->分析ec文件.

自动化执行

可以结合monkey和UI自动化,我简单写了个shell脚本.从编译app、启动app、app至于后台、自动展示jacoco报告

#!/usr/bin/env bash
#当前在环境为Project/app目录

apk_path=`pwd`/app/build/outputs/apk/app-debug.apk
report_path=`pwd`/reporter/index.html

echo "打包app"
gradle assembleDebug
adb uninstall com.weex.jasso
echo "安装app"
adb install ${apk_path}
echo "启动app"
adb shell am start -W -n com.weex.jasso/.Test1Activity -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -f 0x10200000
sleep 2
echo "关闭app"
adb shell am force-stop com.weex.jasso

rm -rf `pwd`/new.ec
rm -rf `pwd`/report
adb pull /sdcard/jacoco/coverage.ec `pwd`/new.ec

macaca coverage -r java -f `pwd`/new.ec -c `pwd`/app/build/intermediates/classes/debug -s `pwd`/app/src/main/java --html `pwd`/reporter
echo "jacoco报告地址:"${report_path}
open -a "/Applications/Safari.app" ${report_path}

效果


macaca coverage生产报告

使用gradle的jacocoTestReport也可以生产报告,也是大多人使用的方式,本文就不做介绍了,主要介绍使用macaca coverage方法.

macaca coverage可以生成jacoco报告,不仅可以生成Android项目,也可以生产iOS、web项目.具体使用请查看https://macacajs.github.io/zh/coverage.

安装macaca-coverage命令:

npm i macaca-cli -g
macaca coverage -h
npm i macaca-coverage --save-dev
macaca coverage命令:
macaca coverage -r java -f `pwd`/new.ec -c `pwd`/app/build/intermediates/classes/debug -s `pwd`/app/src/main/java --html `pwd`/reporter

项目代码

https://github.com/xinxi1990/jacocodemo.git

在项目根目录有个jacaco_test.sh,可以完成自动化测试.

学习帖

https://blog.csdn.net/qq_27103959/article/details/74549964

https://blog.csdn.net/qq_28709925/article/details/51242081

https://www.cnblogs.com/xiajf/p/3993599.html

共收到 8 条回复 时间 点赞
思寒_seveniruby 将本帖设为了精华贴 12月01日 18:36
2楼 已删除

666 学习了

楼主请问一下:

手工执行

安装app->操作app->app至于后台->分析ec文件.

这里操作app需要通过InstrumentationTestRunner 来启动吗?我这边实践,手动启动后,就会报错jacoco的实例找不到,但是通过InstrumentationTestRunner启动就没有问题。请问楼主知道这可能是什么原因吗?

剪烛 回复

不需要啊,InstrumentationTestRunner需要在代码里边配置,很麻烦的.直接手工启动app就行.

请问大神。,多个*.ec文件需要合并成一个时要怎么做呢

想哭……楼主我用您的demo跑了,除了改了gradle插件的版本和gradle版本以外,基本都没改。但是也还是出现了(我自己的demo也是如此)

12-12 18:39:11.801 4020-4020/com.monkey.myapplication E/JacocoUtils: generateEcFile: java.lang.ClassNotFoundException: org.jacoco.agent.rt.RT
12-12 18:39:11.801 4020-4020/com.monkey.myapplication E/JacocoUtils: generateEcFile: org.jacoco.agent.rt.RT
12-12 18:39:11.801 4020-4020/com.monkey.myapplication E/JacocoUtils: generateEcFile: [Ljava.lang.StackTraceElement;@7762a11

想死的心情都有了。。。。。您知道可能是什么原因吗?

小小南瓜 回复

暂时还不知道怎么合并,ec中记录很多信息,怕是再合并出问题了

剪烛 回复

gralde里边有个debug开关 你看开了吗?

xinxi 回复

我好像确定了,我使用gradle plugin 2.3.1 gradle 3.3 这个配套是有这个问题,但是我换成gradle plugin 2.2.2 gradle 2.14.1 就可以了,基本确定是gradle版本引起的。

simple 专栏文章:[精华帖] 社区历年精华帖分类归总 中提及了此贴 12月13日 14:44
ivy520 [该话题已被删除] 中提及了此贴 12月16日 22:55
ivy520 定向班第一期_shell 课程实战_20181216 中提及了此贴 12月16日 23:53
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册