Jacoco集成步骤
1. 在gradle中引入jacoco加入以下:
apply plugin: 'jacoco'
....
jacoco{
toolVersion = "0.7.1.201405082137"
}
android {
buildTypes {
debug {
testCoverageEnabled = true
}
2.接下来加入UI测试脚本
androidTestCompile 'com.jayway.android.robotium:robotium-solo:5.1'
3.在gradle文件中加入如下脚本,来对生成的.ec文件和编译产生的.class文件进行整合形成代码覆盖率报告。
//这里定义为你想要进行代码覆盖率统计的java目录
defcoverageSourceDirs = [
'../app/src/main/java'
]
taskjacocoTestReport(type: JacocoReport) {
group = "Reporting"
description = "Generate Jacoco coverage reportsafter running tests."
//这里为生成报告的类型
reports {
xml.enabled = true
html.enabled = true
}
//这里为编译后产生的.class文件目录,excludes为你不想进行代码覆盖率统计的文件
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('$$','$'))
}
}
}
}
整合后,完整的gradle文件如下:
apply plugin: 'jacoco'
jacoco {
toolVersion = "0.7.4+"
}
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "com.zdj.method2"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
debug {
testCoverageEnabled = true
}
}
}
dependencies {
compile fileTree(dir: 'libs', include:['*.jar'])
compile 'com.android.support:appcompat-v7:23.2.1'
testCompile 'org.robolectric:robolectric:3.0-rc3'
}
def coverageSourceDirs = [
'F:/Myproject/app/src/main/java'
]
task jacocoTestReport(type: JacocoReport) {
group = "Reporting"
description = "Generate Jacococoverage reports after running tests."
reports {
xml.enabled = true
xml.destination="F:/Myproject/reports/jacoco.xml"
html.enabled = true
html.destination="F:/Myproject/reports/jacoco.html"
}
classDirectories = fileTree(
dir: 'F:/Myproject/app/build/intermediates/classes/debug',
excludes: ['**/R*.class',
'**/*$InjectAdapter.class',
'**/*$ModuleAdapter.class',
'**/*$ViewInjector*.class'
])
sourceDirectories =files(coverageSourceDirs)
executionData = files("F:/Myproject/coverage.ec")
doFirst {
new File("F:/Myproject/app/build/intermediates/classes/").eachFileRecurse{ file ->
if (file.name.contains('$$')){
file.renameTo(file.path.replace('$$','$'))
}
}
}
}
4.接下来进入java的设置
将如下代码加入到自己的包中
public class JacocoInstrumentation extends Instrumentation {
public static String TAG = "JacocoInstrumentation:";
private static String DEFAULT_COVERAGE_FILE_PATH = "/mnt/sdcard/DingtoneDebug/coverage.ec";
private static JacocoInstrumentation sInstrumentation;
private final Bundle mResults = new Bundle();
private Intent mIntent;
private static final boolean LOGD = true;
private boolean mCoverage = true;
private String mCoverageFilePath;
/**
* Constructor
*/
public JacocoInstrumentation() {
}
/**
* Called when the instrumentation is starting, before any application code
* has been loaded. Usually this will be implemented to simply call
* {@link #start} to begin the instrumentation thread, which will then
* continue execution in
*/
@Override
public void onCreate(Bundle arguments) {
Log.d(TAG, "onCreate(" + arguments + ")");
super.onCreate(arguments);
sInstrumentation = this;
if (arguments != null) {
//参数在dos里输入 -e coverage true -e coverageFile ....
mCoverage = getBooleanArgument(arguments, "coverage");
mCoverageFilePath = arguments.getString("coverageFile");
}
//将第一个加载的activity设置给intent
mIntent = new Intent(getTargetContext(), getLaunchActiviyClass());
mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//启动一个新的线程,这个线程会调用哦你start();
start();
}
@Override
public void onStart() {
if (LOGD)
Log.d(TAG, "onStart()");
super.onStart();
Looper.prepare();
//与第一个加载的activity同步启动
startActivitySync(mIntent);
}
public static void generateJacocoReport() {
if (sInstrumentation != null) {
sInstrumentation.startGenerateCoverageReport();
}
}
/**
* 获取dos中输入的Boolean参数,如果没有输入则返回false
* @param arguments
* @param tag
* @return
*/
private boolean getBooleanArgument(Bundle arguments, String tag) {
String tagString = arguments.getString(tag);
// return tagString != null && Boolean.parseBoolean(tagString);
return true;
}
/**
* 获得文件路径,可自定义
* @return
*/
private String getCoverageFilePath() {
if (mCoverageFilePath == null) {
// SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US);
// String date = sDateFormat.format(new java.util.Date());
return SDCardUtil.RootDirPathOnSdcard + "coverage.ec";
} else {
return mCoverageFilePath;
}
}
/**
* 产生报告,Out=new FileOutputStream(path,true)
* 第二个参数一定要为true,否则不能持续收集,每次都会覆盖掉上一次
*/
private void generateCoverageReport() {
Log.d(TAG, "generateCoverageReport():" + getCoverageFilePath());
OutputStream out = null;
try {
out = new FileOutputStream(getCoverageFilePath(), 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) {
Log.d(TAG, e.toString(), e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private boolean setCoverageFilePath(String filePath) {
if (filePath != null && filePath.length() > 0) {
mCoverageFilePath = filePath;
return true;
}
return false;
}
private void reportEmmaError(Exception e) {
reportEmmaError("", e);
}
private void reportEmmaError(String hint, Exception e) {
String msg = "Failed to generate emma coverage. " + hint;
Log.e(TAG, msg, e);
mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT, "\nError: "
+ msg);
}
private void startGenerateCoverageReport() {
if (LOGD)
Log.d(TAG, "onActivityFinished()");
if (mCoverage) {
generateCoverageReport();
}
finish(Activity.RESULT_OK, mResults);
}
protected Class<?> getLaunchActiviyClass() {
return SplashActivity.class;
}
}
5.在manifest文件中加入如下:
<instrumentation
android:name="com.zdj.method2.test.JacocoInstrumentation"
android:handleProfiling="true"
android:label="CoverageInstrumentation"
android:targetPackage="com.zdj.method2" />
6.最后在你想要结束测试的地方,调用该类的 generateJacocoReport()的方法,一般是放在主界面按下Back键的时候调用该方法来结束测试,生成代码覆盖率统计。
参考链接:
http://www.dzwanli.com.cn/?p=1374
http://blog.csdn.net/itfootball/article/details/45618609
Android Jacoco使用步骤
1.安装应用到手机,然后关闭应用。
2.在cmd命令行通过输入以下命令来打开应用
Adbshell am instrument -e coverage true -wme.dingtone.app.im.debug/me.dingtone.app.im.instrumentation.JacocoInstrumentation
3.执行测试,
4.测试结束后,点back退出应用到后台
5.执行 adb pull/sdcard/DingtoneDebug/coverage.ecf:/project/dingtone_lib/build/outputs/code-coverage/connected/coverage.ec 将文件从手机sdcard复制到自己的dingtone项目下
图1. 我的项目目录
6.到达工程目录cd f:/project(我的dingtone工程目录是f:/project)
7.执行gradlew jacocoTestReport
8.成功后测试报告存储在:F:\project\dingtone_lib\build\reports\jacoco\jacocoTestReportt\html\index.html,根据报告可以分析代码的执行率。红色标记的是没有执行的代码。
注:
(目前需要有Dingtone的项目工程文件)
经测试可多次进行代码覆盖率统计,每次在主界面按下back键会结束统计,控制台会输出INSTRUMENTATION_CODE: -1。通过back结束测试后,可通过步骤2重新开始测试。每一次的测试报告不会覆盖上一次的,会累加到上一次中。
测试过程中可以通过home进入后台,可以通过图标直接返回App进行测试。
有的手机可能会收集不到代码覆盖率,如果出现覆盖率为0的现象,可以换一只手机(经测试魅族手机可能不支持)。
0 0