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

易寒 · 2015年05月10日 · 最后由 小敏 回复于 2018年06月19日 · 3064 次阅读

之前的覆盖率方法,是不可控的,今天我们来看看思寒说的方式,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方法就可以了。

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

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

我年初也根据这篇文章,写成一个手工统计覆盖率的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日 作者

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

@doctorq android studio 1.1.0 android sdk 22

易寒 #13 · 2015年05月11日 作者

#12楼 @qiqini_w 按返回键了么

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

易寒 #15 · 2015年05月11日 作者

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

@doctorq 好的 谢了 我再看看

易寒 #17 · 2015年05月11日 作者

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

18楼 已删除

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

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

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

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

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

易寒 #24 · 2016年01月21日 作者

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

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

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

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

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

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

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

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

易寒 #32 · 2016年03月21日 作者

#31楼 @max 解决就好

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

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

易寒 #36 · 2016年04月28日 作者

#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被覆盖的问题解决了吗?遇到同样问题😫

#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日 作者

#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 这个要如何定位问题

小灰灰 回复

你好,你的有数据了吗

这个问题已经解决了

@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本身的问题

阿甘 回复

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

autotester1 回复

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

jing 回复

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

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