测试覆盖率 Jacoco 统计 Android 代码覆盖率 [instrument 方式]

在路上 · October 09, 2018 · Last by ningboRainbow replied at June 20, 2023 · 11963 hits
本帖已被设为精华帖!

示例 JacocoTestForAndroid 见:https://github.com/OnTheWay111/JacocoTestForAndroid
还有一个更灵活的非 instrument 方式: [定制触发条件] jacoco 统计 Android 代码覆盖率
本文两年前的代码,有些代码可能已过时,如果遇到问题,可参考:https://www.jianshu.com/p/ab2be01d7347#comment-60999311

1、背景

最近团队想基于 monkey,进行 app 遍历开发,为了与市场上的遍历方案做对比,以及团队遍历方案的优化比较,故选择了 APP 的代码覆盖率统计。

备注:Instrumentation 方式 1 和 Instrumentation 方式 2 几乎一样

2、代码覆盖率方案选型

目前 Java 常用覆盖率工具 Jacoco、Emma、Cobertura 和 Clover(商用)

Jacoco 是一个开源的覆盖率工具。Jacoco 可以嵌入到 Ant 、Maven 中,并提供了 EclEmma Eclipse 插件,也可以使用 Java Agent 技术监控 Java 程序。很多第三方的工具提供了对 Jacoco 的集成,如:Sonar、Jenkins、IDEA.
综合考虑:选择 jacoco 作为代码覆盖率统计工具。

3、jacoco 注入原理


本次,在尽量避免修改源代码的情况下,我们选择 instrumentation 模式,开始操作。

4、jacoco 注入实操 (instrument 方式)

4.1 创建一个安卓项目

随便创建一个简单的安卓项目

4.2 在代码主路径下新建 test 文件夹,新建 3 个类文件

在 java/下新建 test 文件夹,然后在 test 路径下
新建三个类文件,分别是抽象类 FinishListener,Instrumentation 的 Activity 和 instrumentation 类。

  • FinishListener:
public interface FinishListener {
    void onActivityFinished();
    void dumpIntermediateCoverage(String filePath);
}
  • InstrumentedActivity:
public class InstrumentedActivity extends MainActivity {
    public static String TAG = "IntrumentedActivity";
    private FinishListener mListener;
    public void setFinishListener(FinishListener listener) {
        mListener = listener;
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG + ".InstrumentedActivity", "onDestroy()");
        super.finish();
        if (mListener != null) {
            mListener.onActivityFinished();
        }
    }
}
  • JacocoInstrumentation:
public class JacocoInstrumentation extends Instrumentation implements FinishListener {
    public static String TAG = "JacocoInstrumentation:";
    private static String DEFAULT_COVERAGE_FILE_PATH = "/mnt/sdcard/coverage.ec";
    private final Bundle mResults = new Bundle();
    private Intent mIntent;
    private static final boolean LOGD = true;
    private boolean mCoverage = true;
    private String mCoverageFilePath;

    public JacocoInstrumentation() {
    }

    @Override
    public void onCreate(Bundle arguments) {
        Log.d(TAG, "onCreate(" + arguments + ")");
        super.onCreate(arguments);
        DEFAULT_COVERAGE_FILE_PATH = getContext().getFilesDir().getPath().toString() + "/coverage.ec";
        File file = new File(DEFAULT_COVERAGE_FILE_PATH);
        if(!file.exists()){
            try{
                file.createNewFile();
            }catch (IOException e){
                Log.d(TAG,"新建文件异常:"+e);
                e.printStackTrace();}
        }
        if(arguments != null) {
            mCoverageFilePath = arguments.getString("coverageFile");
        }
        mIntent = new Intent(getTargetContext(), InstrumentedActivity.class);
        mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        start();
    }

    @Override
    public void onStart() {
        super.onStart();
        Looper.prepare();
        InstrumentedActivity activity = (InstrumentedActivity) startActivitySync(mIntent);
        activity.setFinishListener(this);
    }

    private String getCoverageFilePath() {
        if (mCoverageFilePath == null) {
            return DEFAULT_COVERAGE_FILE_PATH;
        } else {
            return mCoverageFilePath;
        }
    }

    private void generateCoverageReport() {
        Log.d(TAG, "generateCoverageReport():" + getCoverageFilePath());
        OutputStream out = null;
        try {
            out = new FileOutputStream(getCoverageFilePath(), false);
            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();
                }
            }
        }
    }

    @Override
    public void onActivityFinished() {
        if (LOGD)      Log.d(TAG, "onActivityFinished()");
        if (mCoverage) {
            generateCoverageReport();
        }
        finish(Activity.RESULT_OK, mResults);
    }

    @Override
    public void dumpIntermediateCoverage(String filePath) {

    }
}

4.3 修改 Build.gradle 文件

增加 jacoco 插件和打开覆盖率统计开关

apply plugin: 'com.android.application'
apply plugin: 'jacoco'

android {
    compileSdkVersion 28
    buildToolsVersion "28.0.2"
    defaultConfig {
        applicationId "com.example.app1"
        minSdkVersion 24
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            /**打开覆盖率统计开关*/
             testCoverageEnabled = true
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

4.4 在 manifest.xml 增加声明

  • 在中声明 InstrumentedActivity:
<activity android:label="InstrumentationActivity"    android:name="coverage.netease.com.test.InstrumentedActivity" />
  • 在 manifest 中声明使用 SD 卡权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  • 最后,在 manifest 中单独声明 JacocoInstrumentation:
<instrumentation
        android:handleProfiling="true"
        android:label="CoverageInstrumentation"
        /*这里android:name写明此instrumentation的全称*/
        android:name="com.example.test.JacocoInstrumentation"
        /*这里android:targetPackage写明被测应用的包名*/
        android:targetPackage="com.example.app1"/>

4.5 通过 gradle installDebug 安装到设备上

4.6 在命令行下通过 adb shell am instrument 命令调起 app,具体命令是:

adb shell am instrument com.example.app1/com.example.test.JacocoInstrumentation

也就是 adb shell am instrument /

4.7 调起 app 后我们就可以进行手工测试了,测试完成后点击返回键退出 app。

此时,jacoco 便将覆盖率统计信息写入/data/data//files/coverage.ec 文件。
接下来我们需要新增 gradle task,分析覆盖率文件生成覆盖率 html 报告。

5、生成 html 报告

5.1 首先将 coverage.ec 文件拉到本地,置于指定目录下。

  • adb shell 进入设备,找到 data/data//files/coverage.ec 文件。
  • adb pull 到 pc 本地。
  • 将该文件拖至入 app 根目录/build/outputs/code-coverage/connected(没有的话,可以执行 gradle createDebugCoverageReport)

5.2 新增 gradle task,修改 build.gradle 文件为:

apply plugin: 'com.android.application'
apply plugin: 'jacoco'

android {
    compileSdkVersion 28
    buildToolsVersion "28.0.2"
    defaultConfig {
        applicationId "com.example.app1"
        minSdkVersion 24
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            /**打开覆盖率统计开关*/
             testCoverageEnabled = true
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

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/app_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/app_classes/").eachFileRecurse { file ->
            if (file.name.contains('$$')) {
                file.renameTo(file.path.replace('$$', '$'))
            }
        }
    }
}

5.3 生成 html 报告

  • 执行:
gradle jacocoTestReport

执行完以上命令后,html 报告生成

  • 查看 html 报告 报告路径://app/build/reports/jacoco/jacocoTestReport/html

打开报告后,如图所示:

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 79 条回复 时间 点赞

需要注意的是:build.gradle 文件中,app_classes 的名字需要和项目本身实际匹配,如果不匹配,会出现 FileNotFoundException

请教一个问题,当我把 instrumentation 在 manifest 里面申明以后,运行老报错,说不识别 instrumentation 这个资源,这个是啥问题,如何解决?

西门吹牛 回复

给一个截图或者 log 信息看看吧

@chenhengjie123 老大,请教一下,kotlin 实现的 Android 应用也能用 Jacoco 实现手工测试覆盖率的收集吗?

恒温 将本帖设为了精华贴 25 Nov 08:26

手把手教程,如果能有个视频就更好了

不二家 回复

kotlin 可以和 java 无缝配合,所以可以做的

在路上 回复

如果单单只想测 kotlin 写的一个 sdk,但是这个 sdk 在另一个项目 A 中引用,且需要在 A 中测试 sdk 的测试覆盖率,不知道有什么好实现的思路?谢谢。

不二家 回复

把 jacoco 的配置写在 sdk 项目的 gradle 配置文件中,我们的 SDK 就是这样做的

simple 回复

👍 正解

在路上 回复

然后呢?在主项目的 build.gradle 中不需要配置 jacoco 的配置吗?能否多讲点大致的步骤?另,我们的 sdk 是以 jar 包形式引入主项目中的。

不二家 回复

@simple @ 不二家的小球迷 simple 兄弟,可以给小球迷详细说说具体步骤吗?我没这么实践过,只是理论上觉得是可以的

不二家 回复


参考一下这个

simple 回复

@zailushang @simple 感谢,我先尝试一下,统计主项目的覆盖率的内容,然后尝试 sdk

不二家 回复

我们的 sdk 是插件化多模块的方式,收集覆盖率信息会更麻烦一些,后面可以出一篇文章发到社区里,你试一下先

@simple @zailushang 我在主项目内尝试 jacoco,然后已经安装好 apk,以后然后启动服务的时候报错了:

adb shell am instrument com.xiaomi.aaa/com.xiaomi.aaa.test.JacocoInstrumentation
android.util.AndroidException: INSTRUMENTATION_FAILED: com.xiaomi.aaa/com.xiaomi.aaa.test.JacocoInstrumentation
 at com.android.commands.am.Am.runInstrument(Am.java:956)
 at com.android.commands.am.Am.onRun(Am.java:316)
 at com.android.internal.os.BaseCommand.run(BaseCommand.java:47)
 at com.android.commands.am.Am.main(Am.java:99)
 at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
 at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:330)
不二家 回复

能把所有报错都发出来吗?这段看不太出来原因

在路上 回复

这就是所有报错唉,我在命令行已经安装应用成功,然后用这个命令adb shell am instrument命令调起 app 的时候报错

不二家 回复

在 manifast 中注册 InstrumentedActivity 了吗? 你是小米的?

在路上 回复

我不是小米的,我确认了一遍配置,没有配错。我对这个报错很懵逼啊

我参考这个地址 https://stackoverflow.com/questions/14269687/android-util-androidexception-instrumentation-failed ,然后使用命令
adb shell pm list instrumentation,的确没有看到我安装的应用包名。

不二家 回复

方便的话,可以把你的代码发过来吗?不然这样不好解决

在路上 回复

代码应该不行,我还是自己再看看吧,谢谢。

不二家 回复

方便私聊不,想跟你 QQ 一下,872489864 方便不?

不二家 回复

可以微信,QQ 一般不登录,TTMMD155

@simple 方便增加点描述关于 sdk 覆盖率收集吗?

@simple 大哥,实在想知道,如何统计以 jar 包引入的 sdk 覆盖率的统计?能多点介绍吗?

不二家 回复

最近在搞绩效,等下周找时间写篇教程哈

simple 回复

简单介绍一下,不需要多复杂的配置。粗略的让我知道有点方向。非常感谢大哥。

匿名 #30 · December 07, 2018
不二家 回复

请邮件给我,可以对你提供些帮助😃

邮件给你什么?

匿名 · #32 · December 07, 2018
Author only
不二家 回复

我找我们团队的专家(VPDong)来帮你解答一下,你俩聊

simple 回复

thanks

邮件已发。

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

小小南瓜 回复

我没用过,但是 jacoco 有个命令行工具可以 merge,

simple 专栏文章:[精华帖] 社区历年精华帖分类归总 中提及了此贴 13 Dec 14:44
ivy520 [Topic was deleted] 中提及了此贴 16 Dec 22:55
ivy520 [Topic was deleted] 中提及了此贴 16 Dec 23:53
安涛 [Topic was deleted] 中提及了此贴 21 Dec 16:29

请问从 gitlab 拉下来的代码,出现这个问题是什么原因?

java.lang.ClassNotFoundException: org.jacoco.agent.rt.RT
 java.lang.ClassNotFoundException: org.jacoco.agent.rt.RT
     at java.lang.Class.classForName(Native Method)
     at java.lang.Class.forName(Class.java:400)
     at java.lang.Class.forName(Class.java:326)
     at com.example.test.JacocoInstrumentation.generateCoverageReport(JacocoInstrumentation.java:72)
     at com.example.test.JacocoInstrumentation.onActivityFinished(JacocoInstrumentation.java:94)
     at com.example.test.InstrumentedActivity.onDestroy(InstrumentedActivity.java:22)
     at android.app.Activity.performDestroy(Activity.java:7082)
     at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1154)
     at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:4322)
     at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:4353)
     at android.app.ActivityThread.-wrap6(ActivityThread.java)
     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1618)
     at android.os.Handler.dispatchMessage(Handler.java:102)
     at android.os.Looper.loop(Looper.java:163)
     at android.app.ActivityThread.main(ActivityThread.java:6385)
     at java.lang.reflect.Method.invoke(Native Method)
     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:904)
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
xiaqing 回复

从报错信息看,是 jacoco 的类没有找到,在这里直接引入 jacoco 就行,没有明白你为什么从 gitlab 上拉代码

在路上 专栏文章:[定制触发条件] jacoco 统计 Android 代码覆盖率 中提及了此贴 01 Jan 22:14

请教一下,生成的报告在手机本地可以自动上传吗,如果不能自动上传的话是手工导出来吗,如果 app 重装了,那这个覆盖率的数据不是没有用了?因为基本测试的时候是多个人用不同的手机在测试,如何体现这个版本的 app 的覆盖率呢?

迷糊群 回复

可以自动上传,什么方式都可以
也可以手动导出
app 重装不重装,跟覆盖率没什么关系吧?本文统计的是代码覆盖率。

迷糊群 回复

不同手机的,可以把不同手机的覆盖率文件下载、合并,然后再计算覆盖率

在路上 回复

自动上传是用的什么方式呢,能说详细一点吗?感谢

迷糊群 回复

我没有做自动上传,因为每次的覆盖率文件比较大。
大文件建议用 FTP 上传吧

你好 我在查看覆盖率报告时发现报告没有行覆盖率,请问你有遇到过这关问题吗

在路上 #53 · March 01, 2019 Author
小小南瓜 回复

意思是,点进去以后,看不到哪些代码覆盖了,哪些代码没有覆盖吗?

在路上 回复

是的 ,想看到具体哪些行被覆盖了,但是我的报告只能看到方法级别的覆盖率

在路上 #55 · March 04, 2019 Author
小小南瓜 回复

检查一下这两个路径是否是有效的路径

@zailushang 感谢大神!!

InstrumentedActivity 是不是只能继承标识为 android.intent.action.MAIN 的 Activity,不一定是 ManiActivity 对吧?

不是,用这个方式配置吧,比 Instrument 方式好,更灵活:https://testerhome.com/articles/17546

是用的你说的这个,请问如何实现增量覆盖,我查了很多资料,还使用过 diff-cover 工具,还是没实现增量覆盖率

花开 Android 测试增量和全量覆盖率实践 中提及了此贴 06 Dec 16:05
simple 回复

求问,我们 Android 自动化是每执行一条 case 就杀进程,杀了进程就没有代码覆盖记录了。如何统计啊?现在全部都做完了发现了这个问题。求助感谢

jiawei0113 回复

杀进程之前,执行覆盖率文件生成逻辑。

在路上 回复

每次杀进程之前都执行生成 ec 文件,然后我们大概一百多条 case,这样会生成 100 多个.ec 文件?然后再合并?

jiawei0113 回复

这个怎么成本低,怎么来吧

求助,找了一天没有解决这个问题!!!
在单模块项目里,我按照上面操作成功了。在多模块安装项目里,我把上述文件都放到主模块了,然后使用 adb shell am instrument 出现如下报错:

执行 adb shell am instrument 启动 app 闪退是啥情况呢?

在路上 #69 · April 10, 2020 Author
晓只 回复

看日志

在路上 回复

嗯 找到问题了。。用错模版了 用了 basic 的模版。。

adb pull /data/data/xxx/files/coverage.ec(Permission denied)怎么办,我打印出来在 /data/user/0/xx/files/coverage.ec 下,但是还是 Permission denied,换成/sdcard/下,加了权限,还是 Permission denied😭

这是米 8,安卓 9 系统这样。换个 5.0 手机,pull 文件的时候提示:does not exist

在路上 #72 · April 21, 2020 Author
蓝蓝 回复

首先,建议写到 sdcard 中,如果写到包 data 路径下,包被卸载后,.ec 文件会被删除
关于权限问题,可以放到 Cache 路径下,getExternalCacheDir() + "/“,我验证了,安卓 10 也没有权限问题

关于 getExternalCacheDir() 的具体路径信息,可以打印查看

在路上 回复

可以的,灰常感谢哇!👍

楼主,我还有个问题呀,android.support.* 这种的怎么屏蔽掉?说是 gradle excludes 不管用导致的?

在路上 #75 · April 22, 2020 Author
蓝蓝 回复

看看代码?这样说,不知道具体是什么

在路上 回复

我使用 adb shell am instrument 后 app 闪退了

看看 logcat 的报错

能生产 coverage.ec 了,但是使用上面方法生产报告方法是失败的

在路上 回复

使用了你的配置 构建失败 代码提示这几个方法 classDirectories sourceDirectories executionData 过时

哦哦,那应该是库方法更新了导致的吧,毕竟这代码两年前的了。
辛苦将替换代码留言一下,我在文章中更新一下,避免误导了后面的同行

在路上 回复

小米和红米完全不得行 root 过 但是 data 文件夹路径里面没有创建 com.xxx.xx

在线调试看看 log

还有使用真机 adb shell am instrument com.xxx.gotoheipi/com.xxx.gotoheipi.test.JacocoInstrumentation 手机也 root 过 小米 10 完全没反应 如果用模拟器还行

activity android:label="InstrumentationActivity"    android:name="coverage.netease.com.test.InstrumentedActivity" />

这个写错了吧,name 应该是包名.test.InstrumentedActivity 吧

Shen [Topic was deleted] 中提及了此贴 15 Jun 20:23

@zailushang 大佬求问,我不能生成覆盖率报告是为什么,这个问题已经困扰我四天了,嘤嘤嘤,找了网上的解决方案,都没有效,特来请教
build.gradle 的配置如下

apply plugin: 'com.android.application'
apply plugin: 'jacoco'

jacoco {
    toolVersion = "0.7.9"
//    reportsDir = file("$buildDir/reports/coverage")
}

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"
    defaultConfig {
        applicationId "com.example.sample1"
        minSdkVersion 17
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
        debug {
            /**打开覆盖率统计开关*/
            testCoverageEnabled = true
        }
    }
//    lintOptions {
//        abortOnError false
//    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}

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

task jacocoTestReport(type: JacocoReport) {
    println "aa"
    group = "Reporting"
    description = "Generate Jacoco coverage reports after running tests."
    reports {
        xml.enabled = true
        html.enabled = true
//        html.destination "${buildDir}/reports/jacocoHtml.html"
//        html.destination(file("${buildDir}/reports/jacocoHtml.html"))

    }

    classDirectories.setFrom(files(classDirectories.files.collect {
        fileTree(dir: './build/intermediates/app_classes/debug',
                excludes: ['**/R.class',
                           '**/R$*.class',
                           '**/*$InjectAdapter.class',
                           '**/*$ModuleAdapter.class',
                           '**/*$ViewInjector*.class'
                ])
    }))
    sourceDirectories.from files([coverageSourceDirs])
    executionData.from files("$buildDir/outputs/code_coverage/debugAndroidTest/connected/coverage.ec")


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

manifest 文件配置如下

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.sample1">
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <instrumentation
        android:handleProfiling="true"
        android:label="CoverageInstrumentation"
        android:name="com.example.sample1.test.JacocoInstrumentation"
        android:targetPackage="com.example.sample1"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

        </activity>
        <activity
            android:name="com.example.sample1.loginActivity"
            android:parentActivityName="com.example.sample1.MainActivity">

            <!-- The meta-data tag is required if you support API level 15 and lower -->
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.example.sample1.MainActivity" />
        </activity>
        <activity android:name="com.example.sample1.zhuceActivity"
            android:parentActivityName="com.example.sample1.MainActivity" >
            <!-- The meta-data tag is required if you support API level 15 and lower -->
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.example.sample1.MainActivity" />
        </activity>

        <activity android:label="InstrumentationActivity"
            android:name="com.example.sample1.test.InstrumentedActivity" />
    </application>

</manifest>

build 后的文件夹路径如下:

点击 jacocoTestReport 后,提示 build 成功,但是不生成 html 结果

另外第二个问题:执行 createDebugCoverageReport 时会重新安装 apk 和覆盖率的安装包,然后,删除安装包。那之前编译安装,gradle installDebug 安装和 adb shell am instrument 命令调起 app 那一步还有什么用了呢??ec 会在执行 createDebugCoverageReport 生成新的 ec 文件,且如果没有 sleep 的话,覆盖率都是 0 啊??

第三个问题:
createDebugCoverageReport 会生成 ec 文件和报告,但是需要一直链接 USB 且本地编译 Android 代码。问题来了:createDebugCoverageReport 生成的报告有数据,这个 createDebugCoverageReport 和 jacocoTestReport 的区别是什么?

最最最困惑的问题就是为什么执行 jacocoTestReport 时,不会生成报告

=====================
给后来的人一个答案吧,问题已经解决了。
问题一:不能生成 html 报告的问题:jacoco 版本替换到 0.8.5,gradle3 的编译后的路径是./build/intermediates/javac/debug,不是之前的 app_classes
问题二:createDebugCoverageReport 是 jacoco 中自带的生成报告和路径的命令,但是报告都是 0,需要替换到自己拉出来的 ec 文件。
问题三:jacocoTestReport 才是我们自己写的生成报告的 task

大神打扰了,问下是否有遇到过 kotlin 有一些写法的语句是覆盖不了

大佬你好,我根据上面的帮助内容,在我的代码工程对应的模块 app 下面创建了 jacoco.gradle 文件,并且在同级目录下的 build.gradle 中也进行了引用,并打开了覆盖率统计开关,并在 app/src/main/java/com/下面创建了 jacoco 目录并新增了三个 java 文件,
然后执行了如下三步:
1、编译 ------ 成功,生成 apk
2、上传到手机设备上: adb install -r xxxnternal-dbg.apk。成功上传。
3、开启测试模式:adb shell am instrument com.xx.xx.xx/.jacoco.JacocoInstrumentation 成功!
4、打开手机 app,进行手工操作测试,然后按返回键退出 app
5、检查 /data/data/com.xx.xx.xx/files/xx.ec 文件是否生成,但是!前面都没有问题,就是这一步出现问题了,没有任何对应的 .ec 文件生成,请问可能的原因是哪些呢?


需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up