测试基础 Android 手工测试代码覆盖率增强版

易寒 · 2015年05月10日 · 最后由 第一号伤心人 回复于 2019年06月11日 · 4510 次阅读

之前的覆盖率方法,是不可控的,今天我们来看看思寒说的方式,dump 覆盖率的数据。参考文章

加入代码覆盖率的文件

从参考文章中下载文件,解压缩后有 4 个文件:

.
├── EmmaInstrumentation.java
├── FinishListener.java
├── InstrumentedActivity.java
└── SMSInstrumentedReceiver.java

提取前三个文件加入到我们的源码中,我们的包名为com.wuba.wuxian.android_0504,代码覆盖率的三个文件存放在com.wuba.wuxian.test 包中。

这里写图片描述

然后对三个文件做相应的修改,修改后的文件内容如下:

FinishListener(无修改):

public interface FinishListener {
    void onActivityFinished();
    void dumpIntermediateCoverage(String filePath);
}

JacocoInstrumentation(原 EmmaInstrumentation 文件):

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;


    /**
     * Constructor
     */
    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) {
            //mCoverage = getBooleanArgument(arguments, "coverage");
            mCoverageFilePath = arguments.getString("coverageFile");
        }

        mIntent = new Intent(getTargetContext(), InstrumentedActivity.class);
        mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);        
        start();
    }

    @Override
    public void onStart() {
        if (LOGD)
            Log.d(TAG, "onStart()");
        super.onStart();

        Looper.prepare();
        InstrumentedActivity activity = (InstrumentedActivity) startActivitySync(mIntent);
        activity.setFinishListener(this);
    }

    private boolean getBooleanArgument(Bundle arguments, String tag) {
        String tagString = arguments.getString(tag);
        return tagString != null && Boolean.parseBoolean(tagString);
    }


    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();
                }
            }
        }
    }

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

    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);
    }

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

    @Override
    public void dumpIntermediateCoverage(String filePath){
        // TODO Auto-generated method stub
        if(LOGD){
            Log.d(TAG,"Intermidate Dump Called with file name :"+ filePath);
        }
        if(mCoverage){
            if(!setCoverageFilePath(filePath)){
                if(LOGD){
                    Log.d(TAG,"Unable to set the given file path:"+filePath+" as dump target.");
                }
            }
            generateCoverageReport();
            setCoverageFilePath(DEFAULT_COVERAGE_FILE_PATH);
        }
    }

}


InstrumentedActivity:

public class InstrumentedActivity extends MainActivity {
    public static String TAG = "InstrumentedActivity";

    private FinishListener mListener;

    public void setFinishListener(FinishListener listener) {
        mListener = listener;
    }


    @Override
    public void onDestroy() {
        Log.d(TAG + ".InstrumentedActivity", "onDestroy()");
        super.finish();
        if (mListener != null) {
            mListener.onActivityFinished();
        }
    }

}

修改 AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.wuba.wuxian.android_0504" >

    <!-- To access Google+ APIs: -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!--
 To retrieve OAuth 2.0 tokens or invalidate tokens to disconnect a user. This disconnect
     option is required to comply with the Google+ Sign-In developer policies
    -->
    <uses-permission android:name="android.permission.USE_CREDENTIALS" /> <!-- To retrieve the account name (email) as part of sign-in: -->
    <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <!-- To auto-complete the email text field in the login form with the user's emails -->
    <uses-permission android:name="android.permission.READ_PROFILE" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />

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

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

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

        <activity
            android:name=".GoActivity"
            android:label="@string/title_activity_go" >

        </activity>
        <activity android:label="InstrumentationActivity"
            android:name="com.wuba.wuxian.test.InstrumentedActivity" />
    </application>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <instrumentation
        android:handleProfiling="true"
        android:label="CoverageInstrumentation"
        android:name="com.wuba.wuxian.test.JacocoInstrumentation"
        android:targetPackage="com.wuba.wuxian.android_0504"/>

</manifest>


执行

首先构建 APK,安装到手机上,默认 android 会打开应用,我们需要关闭,再通过一下命令打开应用:

adb shell am instrument com.wuba.wuxian.android_0504/com.wuba.wuxian.test.JacocoInstrumentation

打开应用后,你可以进行测试了

测试完成后,获得代码覆盖率数据

测试完成后,我们要收集代码覆盖率的数据了。其实就是要生成一个 jacoco 的文件,在本次实验中,我们定义的 jacoco 文件为 coverage.ec 文件,存放在应用数据目录下 files 文件夹下,而且生成数据的操作放在应用退出时。

这里写图片描述

你需要将这个 coverage.ec 文件 dump 到本地,然后利用代码结构生成报告,原生 gradle-android-plugin 也是这样一个流程。所以怎么生成报告我就不在详细介绍了。

总结

上面的方式是在不修改源码环境下做的一个类似于后门的方式获得代码覆盖率数据的方式,但是如果你想更加方便的去做这个,比如在应用中长按生成数据,你可以修改自己的源码,在长按事件中调用 JacocoInstrumentation 中 generateCoverageReport 方法就可以了。

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

你好,我执行 adb shell am 的时候,app 闪一下就关闭了,如何将其打开,进行手工测试啊?

onesbyones 回复

我也遇到了这个问题,不知道该怎么解决 能说下最后是怎么解决的嘛??

jing 回复

你好,这个问题解决了吗?可以分享一下解决方案吗?

易寒 回复

我完全是按照上面的步骤来的,但是也出现了 ClassNotFound 的异常,找不到 jacoco.rt.RT,能否给一个解决问题的方向呢?多谢

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

顶一顶我右边的男人,哈哈

我年初也根据这篇文章,写成一个手工统计覆盖率的 Android Service,放在我们产品的测试用的调试选项里,就是 g 构建得用 ant instrument 来构建,sample 在https://github.com/cjtcwyk/AndroidEmmaService

#2 楼 @cjtcwyk 嗯,构建的方式无所谓,你这个更高级点,可以在程序中随时统计覆盖率。

@doctorq 想问一下你是 gradle build 的吧 jacoco 加入 compile 还是报错提示 java.lang.ClassNotFoundException: org.jacoco.agent.rt.RT

D/JacocoInstrumentation:( 1559): onActivityFinished()
D/JacocoInstrumentation:( 1559): generateCoverageReport():/data/data/com.example/files/coverage.ec
D/JacocoInstrumentation:( 1559): java.lang.ClassNotFoundException: org.jacoco.agent.rt.RT
D/JacocoInstrumentation:( 1559): java.lang.ClassNotFoundException: org.jacoco.agent.rt.RT
D/JacocoInstrumentation:( 1559):        at java.lang.Class.classForName(Native Method)
D/JacocoInstrumentation:( 1559):        at java.lang.Class.forName(Class.java:204)
D/JacocoInstrumentation:( 1559):        at java.lang.Class.forName(Class.java:169)
D/JacocoInstrumentation:( 1559):        at com.example.test.JacocoInstrumentation.generateCoverageReport(JacocoInstrumentation.java:83)
D/JacocoInstrumentation:( 1559):        at com.example.test.JacocoInstrumentation.onActivityFinished(JacocoInstrumentation.java:134)
D/JacocoInstrumentation:( 1559):        at com.example.test.InstrumentedActivity.onDestroy(InstrumentedActivity.java:22)
D/JacocoInstrumentation:( 1559):        at android.app.Activity.performDestroy(Activity.java:5302)
D/JacocoInstrumentation:( 1559):        at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1117)
D/JacocoInstrumentation:( 1559):        at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3486)
D/JacocoInstrumentation:( 1559):        at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3517)
D/JacocoInstrumentation:( 1559):        at android.app.ActivityThread.access$1200(ActivityThread.java:141)
D/JacocoInstrumentation:( 1559):        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1309)
D/JacocoInstrumentation:( 1559):        at android.os.Handler.dispatchMessage(Handler.java:99)
D/JacocoInstrumentation:( 1559):        at android.os.Looper.loop(Looper.java:137)
D/JacocoInstrumentation:( 1559):        at android.app.ActivityThread.main(ActivityThread.java:5103)
D/JacocoInstrumentation:( 1559):        at java.lang.reflect.Method.invokeNative(Native Method)
D/JacocoInstrumentation:( 1559):        at java.lang.reflect.Method.invoke(Method.java:525)
D/JacocoInstrumentation:( 1559):        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
D/JacocoInstrumentation:( 1559):        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
D/JacocoInstrumentation:( 1559):        at dalvik.system.NativeStart.main(Native Method)
D/JacocoInstrumentation:( 1559): Caused by: java.lang.NoClassDefFoundError: org/jacoco/agent/rt/RT
D/JacocoInstrumentation:( 1559):        ... 20 more
D/JacocoInstrumentation:( 1559): Caused by: java.lang.ClassNotFoundException: Didn't find class "org.jacoco.agent.rt.

#4 楼 @qiqini_w 要 build

@doctorq 恩 已经 gradlew clean build 的 并重新安装的 apk

@doctorq 我看到参考的那个 blog 里面是 ant instrument 没有影响吧?

#7 楼 @qiqini_w 没影响,我用的是 gradle,按我做的就可以了。

@doctorq 恩 是按照上面的步骤来做的 我回复的错误 log 是 logcat 中的 因为 file 下面的 coverage.ec 文件是 0 字节

易寒 #10 · 2015年05月11日 Author

#9 楼 @qiqini_w build.gradle 的testCoverageEnabled = true 设置了么

@doctorq android studio 1.1.0 android sdk 22

易寒 #13 · 2015年05月11日 Author

#12 楼 @qiqini_w 按返回键了么

@doctorq 直接停的 app 是在 genymotion 运行的

易寒 #15 · 2015年05月11日 Author

#14 楼 @qiqini_w 好好对照我的步骤。

@doctorq 好的 谢了 我再看看

易寒 #17 · 2015年05月11日 Author

#16 楼 @qiqini_w 使用返回键退出应用

根据楼主的文章,一步一步的来,执行到这里 adb shell am instrument...时也确实拉起了被测 apk,但是操作了一个解锁界面后,就直接退出到手机主界面了,不知道怎么回事。

我的 coverage.ec 生成报告的时候总是提示,Unable to read execution data file E:\coverage\app\build\outputs\code-coverage\connected\coverage.ec

大神 已插桩的 APK 数据如何实时收集啊~~

#24 楼 @doctorq 问题已解决,多谢~

@doctorq 按照上面的步骤,在修改完 manifest 文件时出现问题,如下图,没有接触过 instrumentation,求赐教

易寒 #24 · 2016年01月21日 Author

#23 楼 @max jacocoInstrumentation 必须放在测试包里,不是随便取名字的

没接触过这块,晚上回去试试

@doctorq 感谢你的分享,看了这 3 篇关于代码覆盖率的帖子,收获还蛮多,可是这三种办法都有一定的局限性,了解到可以通过修改源代码,安装插桩后的 apk 到真机,即可任意手工测试,最后收集代码覆盖率,请问有什么详细的参考资料吗?

@doctorq ,为啥生成的.ec 文件,执行./gradlew jacocoTestReport 的时候,总是报 Unable to read execution data file /Users/lord_of_dota/gradle/test/build/outputs/coverage.ec 呢?task 内容如下:

#25 楼 @max 你好请问一下,你这个问题是怎么解决的?

#30 楼 @zitong9010 你好,我的那个问题,忽略掉红色的报错就好了,不需要修改什么,可以执行

易寒 #32 · 2016年03月21日 Author

#31 楼 @max 解决就好

生成 coverage.ec 后怎么生成报告?

#34 楼 @doctorq 可以生成报告了,谢谢!我想再请教下,因为现在每次执行统计前都要通过 adb shell am instrument 来启动 app,那么如果我加入 UI 自动化(我这里用 robotium),且只针对 apk 的方式,也就是我想脱离源码工程来执行。那么我每次启动就不可能执行那个 adb 命令。那么你有没什么好的方案。

易寒 #36 · 2016年04月28日 Author

#35 楼 @snail 放在 apk 中,用一个开关控制,adb shell 只是为了执行某个方法,相应的把这个方法用开关去执行就可以了

#36 楼 @doctorq 明白你的意思了,谢谢!

@doctorq , 执行 am instrument 会出现 错误:java.io.FileNotFoundException: /jacoco.exec: open failed: EROFS (Read-only file system) 请问一下,这个如何解决

似乎虽然有这个错误:java.io.FileNotFoundException: /jacoco.exec: open failed: EROFS (Read-only file system) ,也可以生成覆盖率报告。

@doctorq ,请问一下,如果有多个 library module 的情况下,这个覆盖率如何获得呢?

#23 楼 @max Test running failed: Unable to find instrumentation info for: ComponentInf 提示我这个怎么解决

把手机生成的 coverage.ec 文件拷贝到电脑 code-coverage\connected 路径下,执行 gradle task 后,coverage.ec 被覆盖了,为 0 字节,导致生成的 html 覆盖率为 0,困扰了好久,求指教

@maizi coverage.ec 被覆盖的问题解决了吗?遇到同样问题😫

匿名 #44 · 2016年08月29日

#29 楼 @zitong9010 我也遇到了这个问题。。不知道怎么回事

有辦法改成只要離開 APP 就生成 coverage.ec 嗎? 不只是利用 返回鍵離開 APP.....

@DoctorQ
adb shell am instrument com.wuba.wuxian.android_0504/com.wuba.wuxian.test.JacocoInstrumentation
可以传参数进去么,比如命令后面加--es 传个字符串

易寒 #47 · 2016年10月08日 Author

#46 楼 @Felix1 没玩过,应该不行

#46 楼 @Felix1 你可以试试看,从命令来讲是允许的

syl7752 [该话题已被删除] 中提及了此贴 10月24日 19:07
syl7752 jacoco 覆盖率与 git diff 结合的应用实践 中提及了此贴 11月25日 09:49

@ 按照,操作执行 adb shell am instrument coverage.netease.com.androidcoverage/coverage.netease.com.test.JacocoInstrumentation 时,报错,错误信息如下,请问知道原因吗?
android.util.AndroidException: INSTRUMENTATION_FAILED: coverage.netease.com.androidcoverage/coverage.netease.com.test.JacocoInstrumentation
at com.android.commands.am.Am.runInstrument(Am.java:865)
at com.android.commands.am.Am.onRun(Am.java:282)
at com.android.internal.os.BaseCommand.run(BaseCommand.java:47)
at com.android.commands.am.Am.main(Am.java:76)

您好,参考文章里的压缩包下载不了,请问是怎么回事呢

pinkcarrot 回复

这种错误,可能是生成 ec 文件的版本和读取 ec 文件版本不一致,可以使用
jacoco {
toolVersion = "0.7.4+"
}
对 jacoco 版本降级

xiaomi 回复

哥们,你这个问题解决了吗,什么原因

我按照流程设置完了,手机连电脑,然后用 andriod studio 安装应用后,这里会自动启动应用,但是看到有 error 日志, E/dex2oat: Failed to create oat file: /data/dalvik-cache/arm64/data@app@com.pingenie.screenlocker-1@split_lib_slice_9_apk.apk@classes.dex: Permission denied,不知道是什么问题? 后面再用命令启动应用,测试完后返回键退出,手机上没有 coverage.ec 文件

Vincent 回复

你好 下载了你的代码 要如何编译?

jing 回复

你好 我也遇到打桩后的 apk 闪退的问题,请问您找到解决方法了吗?

yangjin 回复

你好! 求助多模块工程如何得到覆盖率信息?

summe 回复

这是很久之前写的 demo 了,之前也是草草写的,没考虑过易用性和扩展性,你如果想用,直接拷到你的 app 里,启动的时候调一下 startEmmaService 拉起 service 就行了

易寒 回复

你好 发现生成的 coverage .ec 为 0k 这个要如何定位问题

18楼 已删除

#16 楼 @qiqini_w 你的 coverage.ec 有数据了吗 我就一次有数据 之后就一直是 0 字节 是按的 back 键退出应用

小灰灰 回复

你好,你的有数据了吗

对酒当歌 回复

刚刚也遇到这个问题,请问如何解决

这个问题已经解决了

@DoctorQ 楼主好,最近在调研jacoco来统计Android的代码覆盖率,使用testerhome客户端的源码来调试的。但是遇到下边的问题,日志如下:

09-06 14:28:13.661 27367-27367/com.testerhome.nativeandroid D/CrashReport: >>> com.testerhome.nativeandroid.views.TopicDetailActivity onDestroyed <<<
09-06 14:28:18.091 27367-27367/com.testerhome.nativeandroid W/Toast: From com.testerhome.nativeandroid, go ahead.
09-06 14:28:18.821 27367-27420/com.testerhome.nativeandroid D/dalvikvm: GC_EXPLICIT freed 3173K, 14% free 32497K/37432K, paused 9ms+8ms, total 79ms
09-06 14:28:19.081 27367-27367/com.testerhome.nativeandroid I/Timeline: Timeline: Activity_launch_request time:1621065528
09-06 14:28:19.101 27367-27367/com.testerhome.nativeandroid D/CrashReport: >>> com.testerhome.test.InstrumentedActivity onPaused <<<
09-06 14:28:19.131 27367-27367/com.testerhome.nativeandroid D/CrashReport: >>> com.testerhome.nativeandroid.views.DummyActivity onCreated <<<
09-06 14:28:19.141 27367-27367/com.testerhome.nativeandroid D/CrashReport: >>> com.testerhome.nativeandroid.views.DummyActivity onResumed <<<
09-06 14:28:19.191 27367-27367/com.testerhome.nativeandroid I/Timeline: Timeline: Activity_idle id: android.os.BinderProxy@43ee8dd0 time:1621065637
09-06 14:28:19.651 27367-27367/com.testerhome.nativeandroid D/CrashReport: >>> com.testerhome.nativeandroid.views.DummyActivity onPaused <<<
09-06 14:28:19.701 27367-27367/com.testerhome.nativeandroid D/IntrumentedPlayer: .InstrumentedActivity: onDestroy()
09-06 14:28:19.701 27367-27367/com.testerhome.nativeandroid D/JacocoInstrumentation:: JacocoInstrumentation: onActivityFinished()
09-06 14:28:19.701 27367-27367/com.testerhome.nativeandroid D/JacocoInstrumentation:: generateCoverageReport path :/data/data/com.testerhome.nativeandroid/files/coverage.ec
09-06 14:28:19.731 27367-27367/com.testerhome.nativeandroid D/JacocoInstrumentation:: java.lang.reflect.InvocationTargetException
                                                                                      java.lang.reflect.InvocationTargetException
                                                                                          at java.lang.reflect.Method.invokeNative(Native Method)
                                                                                          at java.lang.reflect.Method.invoke(Method.java:515)
                                                                                          at com.testerhome.test.JacocoInstrumentation.generateCoverageReport(JacocoInstrumentation.java:86)
                                                                                          at com.testerhome.test.JacocoInstrumentation.onActivityFinished(JacocoInstrumentation.java:135)
                                                                                          at com.testerhome.test.InstrumentedActivity.onDestroy(InstrumentedActivity.java:58)
                                                                                          at android.app.Activity.performDestroy(Activity.java:5421)
                                                                                          at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1117)
                                                                                          at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3494)
                                                                                          at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3525)
                                                                                          at android.app.ActivityThread.access$1500(ActivityThread.java:141)
                                                                                          at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1266)
                                                                                          at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                                          at android.os.Looper.loop(Looper.java:136)
                                                                                          at android.app.ActivityThread.main(ActivityThread.java:5072)
                                                                                          at java.lang.reflect.Method.invokeNative(Native Method)
                                                                                          at java.lang.reflect.Method.invoke(Method.java:515)
                                                                                          at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
                                                                                          at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:609)
                                                                                          at dalvik.system.NativeStart.main(Native Method)
                                                                                       Caused by: java.lang.IllegalStateException: JaCoCo agent not started.
                                                                                          at org.jacoco.agent.rt.internal_932a715.Agent.getInstance(Agent.java:72)
                                                                                          at org.jacoco.agent.rt.RT.getAgent(RT.java:32)
                                                                                          at java.lang.reflect.Method.invokeNative(Native Method) 
                                                                                          at java.lang.reflect.Method.invoke(Method.java:515) 
                                                                                          at com.testerhome.test.JacocoInstrumentation.generateCoverageReport(JacocoInstrumentation.java:86) 
                                                                                          at com.testerhome.test.JacocoInstrumentation.onActivityFinished(JacocoInstrumentation.java:135) 
                                                                                          at com.testerhome.test.InstrumentedActivity.onDestroy(InstrumentedActivity.java:58) 
                                                                                          at android.app.Activity.performDestroy(Activity.java:5421) 
                                                                                          at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1117) 
                                                                                          at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3494) 
                                                                                          at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3525) 
                                                                                          at android.app.ActivityThread.access$1500(ActivityThread.java:141) 
                                                                                          at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1266) 
                                                                                          at android.os.Handler.dispatchMessage(Handler.java:102) 
                                                                                          at android.os.Looper.loop(Looper.java:136) 
                                                                                          at android.app.ActivityThread.main(ActivityThread.java:5072) 
                                                                                          at java.lang.reflect.Method.invokeNative(Native Method) 
                                                                                          at java.lang.reflect.Method.invoke(Method.java:515) 
                                                                                          at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793) 
                                                                                          at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:609) 
                                                                                          at dalvik.system.NativeStart.main(Native Method) 

这个logcat日志是在退出testerhome客户端之后的错误日志,然后看了下jacoco的依赖包源码,发现如下:从RT.class中的

public static IAgent getAgent() throws IllegalStateException {
    return Agent.getInstance();
}

方法调用了Agent.class中的

public class Agent implements IAgent {
    private static Agent singleton;

    ......

    public static synchronized Agent getInstance() throws IllegalStateException {
        if(singleton == null) {
            throw new IllegalStateException("JaCoCo agent not started.");
        } else {
            return singleton;
        }
    }

但是源码中的singleton引用在初始化的是没有引用任何对象,所以一直都会抛出IllegalStateException异常。

实在找不到路子了,所以想问下:是我的覆盖率统计方式出错了,还是jacoco本身的问题

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册