第2.2节 Android Jacoco插件覆盖率采集

        JaCoCo(Java Code Coverage)是一款开源的代码覆盖率分析工具,适用于Java和Android项目。它通过插桩技术统计测试过程中代码的执行情况,生成可视化报告,帮助开发者评估测试用例的有效性。在github上开源的项目: GitHub - jacoco/jacoco: :microscope: Java Code Coverage Library ,是针对服务端的,而移动端的jacoco插件暂时没有开源,可以参考: The JaCoCo Plugin ,使用最多的版本是0.8.7,你也可以尝试使用最新版本。

2.2.1 Jacoco插件的接入

将要接入Jacoco插件的一个Android应用,或是从github上下载一个Demo来进行测试,不过网上的Demo可能因为gradle或是其他包的版本不兼容最新的版本,需要先进行处理一下,能打包后再进行接入jacoco插件。现在我以一个简单的Android计算器的Demo做一个jacoco接入的演示,早期github上的项目地址是 https://github.com/FlamingJay/AndroidCalculator.git,后来被删除了,后面我将上传到我的github上供大家学习。
  • build.gradle中添加jacoco插件
在app下的build.gradle文件中添加对jacoco的引用,如下所示:
plugins {id 'com.android.application'id 'jacoco'
}jacoco {toolVersion = "0.8.7" // 选择合适的版本
}

注意:此处使用的是0.8.7版本,这个版本比较稳定,你也可以使用最新版本。

  • 打开覆盖率采集开关
  • android {...buildTypes {release {minifyEnabled falsetestCoverageEnabled = trueproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}debug {testCoverageEnabled = true}}...}
  • 一般在debug包下进行覆盖率的测试,打开testCoverageEnabled = true, 构建项目的时候就能对代码进行插桩,采集覆盖率数据。
  • release包有代码混淆,覆盖率报告渲染的时候,无法正确对应到类的源码,所以要对release包进行测试时,需要关掉代码混淆。
通过上面的配置,打包后的App就可以采集覆盖率数据,记录用户的具体操作覆盖。注意:此时的覆盖率数据存在于内存中,要想拿到覆盖率数据,必须人为地将覆盖率数据写入到文件中。

2.2.2 覆盖率数据采集

由于覆盖率数据内容存在于手机内存中,当App退出后,内存中的数据将被清空。而我们要进行覆盖率测试的时候,必须要拿到覆盖率数据文件,下面我们将借助于jacoco将覆盖率数据从内容写入到文件中,代码如下:
package com.example.calculator.utils;import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;public class GenerateECFile {public static String TAG = "GenerateECFile:";private static String DEFAULT_COVERAGE_FILE_PATH = "/mnt/sdcard/coverage.ec";private static String partStr="coverage";public static List<File> getDeletePath(Context context) {List<File> files = new ArrayList<>();File sdDir = new File(context.getFilesDir().getPath());String[] list = sdDir.list();if (list != null) {for(int i=0;i<list.length;i++){if(list[i].contains(partStr)){files.add(new File(sdDir.getPath() + "/" + list[i]));}}}return files;}/*** 删除覆率数据文件* @param context*/public static void deleteCoverageFiles(Context context){List<File> files = getDeletePath(context);if (files!= null && files.size() > 0) {for(File file:files){Log.d(TAG, "JacocoUtils_generateEcFile: 清除旧的ec文件path:+"+ file.getPath());// FileUtils.deleteFile(file);boolean result = file.delete();if (!result && file.exists()) {try{throw new IOException("Failed to delete " + file.getAbsolutePath());}catch(IOException e){e.printStackTrace();}}}}}public static void onJacocoCreate(Context context) {Log.d(TAG, "onJacocoCreate");SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss");Calendar cal = Calendar.getInstance();String create_time = format.format(cal.getTime()).substring(0,19);// 获取packagemanager的实例PackageManager packageManager = context.getPackageManager();// getPackageName()是你当前类的包名,0代表是获取版本信息try{//删除原来的覆盖率数据文件deleteCoverageFiles(context);//生成新覆盖率数据文件名PackageInfo packInfo = packageManager.getPackageInfo(context.getPackageName(),0);String app_version = packInfo.versionName;DEFAULT_COVERAGE_FILE_PATH = context.getFilesDir().getPath() + "/coverage"+"-"+app_version+"-"+create_time+".ec";}catch(PackageManager.NameNotFoundException e){e.printStackTrace();Log.d(TAG,"找不到包名"+e);}}/*** 生成覆盖率数据文件* @param context*/public static void generateCoverageFile(Context context) {OutputStream out = null;try {//如果文件不存在,创建覆盖率数据文件File file = new File(DEFAULT_COVERAGE_FILE_PATH);if(!file.exists()){try{file.createNewFile();}catch (IOException e){Log.d(TAG,"新建文件异常:"+e);e.printStackTrace();}}//将内存中的覆盖率数据写入到文件中out = new FileOutputStream(DEFAULT_COVERAGE_FILE_PATH, true);Object agent = Class.forName("org.jacoco.agent.rt.RT").getMethod("getAgent", new Class[0]).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.d(TAG, e.toString(), e);} finally {if (out != null) {try {out.close();} catch (IOException e) {e.printStackTrace();}}}}
}

说明:

  • 本代码借助于jacoco.agent将手机内存中的覆盖率数据文件写入到文件中;
  • 默认文件路径是本应用的files文件夹,由于现在高版本的android系统不允许访问手机存储,只能存储到App的本身空间中,所以在AndroidManifest.xml文件中要添加:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  • 代码中有初始化,清除原来覆盖率文件的函数,也有生成覆盖率文件的函数,只要在适合的时机调用即可。
  • 文件命名:coverage-app版本号-日期-时间.ec 如:coverage-1.0-2025-02-11-10_50_03.ec。

2.2.3 何时生成覆盖率文件?

通过专门的类,可以将手机中的覆盖率数据写入到应用的空间中,保存成覆盖率文件。现在存在一个问题,什么时候保存覆盖率数据文件?由于覆盖率数据存在于内存中,一旦应用退出 ,数据将被清除。分析一下app的生命周期,不难发现:
  • 在app进入前端时,清除原来的覆盖率数据文件,开始采集覆盖率数据;
  • 在app进入后台时,生成覆盖率数据文件。
这样交互进行比较合适。如果你的应用中有生命周期控制类,在相应的函数中引用上面的覆盖率生成函数即可,如果没有,请按如下方法,在MainActivity中的onCreate函数中添加生命周期控制函数,如下所示:
@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);.....// 注册LifecycleObserverProcessLifecycleOwner.get().getLifecycle().addObserver(new LifecycleObserver() {@OnLifecycleEvent(Lifecycle.Event.ON_START)public void onMoveToForeground() {//清除原来的覆盖率数据文件GenerateECFile.onJacocoCreate(MainActivity.this);// 应用从后台移动到前台时调用Log.i(TAG,"App moved to foreground");}@OnLifecycleEvent(Lifecycle.Event.ON_STOP)public void onMoveToBackground() {//开始生成覆盖率数据文件GenerateECFile.generateCoverageFile(MainActivity.this);// 应用从前台移动到后台时调用Log.i(TAG,"App moved to background");}});....}

 添加了以上操作,就可以在App的生命周期中采集覆盖率数据,并写入到文件中。

将通过上面修改的app打包,安装到手机上进行测试。打开计算器,随便进行一些操作或是执行一些测试用例,然后再将app置于后台,注意不要杀死App。此时会将前面操作的覆盖率数据写入数据文件,置到后台一会儿后,再杀死应用,就可以拿覆盖率数据文件了。

2.2.4 下载覆盖率数据文件

根据设置覆盖率数据文件会生成在手机下面的位置:/data/data/应用包名/files,但是正常的手机系统由于安全设置,是无法下载下来的:
此时,在Android Studio点击右侧的Device Manager,找到连接的手机设备,单击设备最右侧的按钮,选择"Open in Device Explorer",就可以打开手机的文件系统,如下所示:
找到对应的覆盖率数据文件的位置,如:/data/data/com.example.calculator/files/,就可以看到覆盖率数据文件,右击文件,选择"save as..".将覆盖率数据文件下载到本地目录。
下载到覆盖率数据文件后,就可以根据需要生成全量和增量覆盖率报告,检测测试情况,排查漏测问题补充测试用例。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/38514.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenGL ES ->乒乓缓冲,计算只用两个帧缓冲对象(Frame Buffer Object)+叠加多个滤镜作用后的Bitmap

乒乓缓冲核心思想 不使用乒乓缓冲&#xff0c;如果要每个滤镜作用下的绘制内容&#xff0c;也就是这个滤镜作用下的帧缓冲&#xff0c;需要创建一个Frame Buffer Object加上对应的Frame Buffer Object Texture使用乒乓缓冲&#xff0c;只用两个Frame Buffer Object加上对应的F…

Unity导出WebGL,无法加载,data文件无法找到 404(NotFound)

问题&#xff1a;data文件无法找到404Not found 示例是使用IIS托管启动 F12可以看到not found 的报错 解决办法&#xff1a; iis无法识别data文件&#xff0c;在MIME类型中增加data 类型&#xff1a;application/octet-stream 添加之后&#xff0c;会在根目录下生产一个…

C++与OO思想的联系

一、C与OO思想的联系 C&#xff1a;OO思想&#xff08;面向对象--属性和行为&#xff09; 任何事务都可以被看做一个个对象&#xff0c;一个再复杂的模型结构都是由千千万万个对象组成。 OO思想两个要素&#xff1a;属性和行为(方法)。 OO思想的特点&#xff1a; 封装&#x…

单表达式倒计时工具:datetime的极度优雅(DeepSeek)

一个简单表达式&#xff0c;也可以优雅自成工具。 笔记模板由python脚本于2025-03-22 20:25:49创建&#xff0c;本篇笔记适合任意喜欢学习的coder翻阅。 【学习的细节是欢悦的历程】 博客的核心价值&#xff1a;在于输出思考与经验&#xff0c;而不仅仅是知识的简单复述。 Pyth…

Kubernetes的Replica Set和ReplicaController有什么区别

ReplicaSet 和 ReplicationController 是 Kubernetes 中用于管理应用程序副本的两种资源&#xff0c;它们有类似的功能&#xff0c;但 ReplicaSet 是 ReplicationController 的增强版本。 以下是它们的主要区别&#xff1a; 1. 功能的演进 ReplicationController 是 Kubernete…

CSS基础知识一览

持续维护 选择器 display 常用属性 浮动 弹性布局

IS-IS原理与配置

一、IS-IS概述 IS-IS&#xff08;Intermediate System to Intermediate System&#xff0c;中间系统到中间系统&#xff09;是ISO&#xff08;International Organization for Standardization&#xff0c;国际标准化组织&#xff09;为它的CLNP&#xff08;ConnectionLessNet…

【前端】Visual Studio Code安装配置教程:下载、汉化、常用组件、基本操作

文章目录 一、Visual Studio Code下载二、汉化三、常用组件1、Auto Rename Tag2、view-in-browser3、Live Server 四、基本操作五、感谢观看&#xff01; 一、Visual Studio Code下载 下载官网&#xff1a;https://code.visualstudio.com/ 进入官网后点击右上角的Download &…

git推送代码相关学习——(一)

推荐去阅读一下廖老师的git相关的教程https://liaoxuefeng.com/books/git/introduction/index.html 这个系列就来学习一下git操作。 第一步&#xff0c;新建项目 去github中新建一个项目&#xff0c;然后依据项目来进行本地的开发工作。 第二步&#xff0c;拉取项目 git c…

CMS网站模板设计与用户定制化实战评测

内容概要 在数字化转型背景下&#xff0c;CMS平台作为企业内容管理的核心载体&#xff0c;其模板架构的灵活性与用户定制能力直接影响运营效率。通过对WordPress、Baklib等主流系统的技术解构发现&#xff0c;模块化设计理念已成为行业基准——WordPress依托超过6万款主题库实…

Maya基本操作

基本操作 按住ALT键&#xff0c;左键旋转视角&#xff0c;中键平移视角&#xff0c;右键放大缩小视角。 按空格键切换4格视图。 导入FBX格式文件后&#xff0c;无贴图显示。 按6键开启。着色纹理显示 坐标轴相关 修改菜单-左键最上面的虚线。固定修改选项窗口。 选中物体…

政安晨【超级AI工作流】—— 使用Dify通过工作流对接ComfyUI实现多工作流协同

政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 目录 一、准备工作 Dify跑起来 ollama局域网化配置 Dify配置并验证 启动ComfyUI 二、…

【蓝桥杯】12111暖气冰场(多源BFS 或者 二分)

思路 这题可以用BFS做&#xff0c;也可以用二分来做。 用二分这里只提供一个思路&#xff1a;对时间来二分查找&#xff0c;check函数就是检查在特定的时间 t 0 t_0 t0​内每一个暖气炉的传播距离能否覆盖所有格子。 用BFS做&#xff1a; 由几个点开始向外扩散&#xff0c;知道…

【云上CPU玩转AIGC】——腾讯云高性能应用服务HAI已支持DeepSeek-R1模型预装环境和CPU算力

&#x1f3bc;个人主页&#xff1a;【Y小夜】 &#x1f60e;作者简介&#xff1a;一位双非学校的大三学生&#xff0c;编程爱好者&#xff0c; 专注于基础和实战分享&#xff0c;欢迎私信咨询&#xff01; &#x1f386;入门专栏&#xff1a;&#x1f387;【MySQL&#xff0…

【JavaEE】网络编程socket

1.❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; Hello, Hello~ 亲爱的朋友们&#x1f44b;&#x1f44b;&#xff0c;这里是E绵绵呀✍️✍️。 如果你喜欢这篇文章&#xff0c;请别吝啬你的点赞❤️❤️和收藏&#x1f4d6;&#x1f4d6;。如果你对我的…

超硬核区块链算法仿真:联盟链PBFT多线程仿真实现 :c语言完全详解版

1 22年年底想用gpt做出一个pbft的算法仿真&#xff0c;到了25年终于可以结合gpt grok perplexcity deepseek等实现了&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 1.1简化版 // 定义 Windows 版本&#xff0c;确保条件变量相关函数可用 #define _WIN32_W…

【并发编程】聊聊forkJoin的原理和最佳实践

对于线程池来说&#xff0c;其实本质就是一个生产者消费者的模式&#xff0c;而通过竞争的方式从队列中获取任务执行。本质上其实就是按照任务级别进行处理&#xff0c;但是对于一些可以分而治之的任务&#xff0c;传统的线程池没有办法分治处理。一是无法对大任务进行拆分&…

【数据预测】基于遗传算法GA的LSTM光伏功率预测 GA-LSTM光伏功率预测【Matlab代码#91】

文章目录 【可更换其他算法&#xff0c;获取资源请见文章第6节&#xff1a;资源获取】1. 遗传算法GA2. 长短期记忆网络LSTM3. 基于GA-LSTM的光伏功率预测4. 部分代码展示5. 运行结果展示6. 资源获取 【可更换其他算法&#xff0c;获取资源请见文章第6节&#xff1a;资源获取】 …

Java 填充 PDF 模版

制作 PDF 模版 安装 OnlyOffice 从 OnlyOffice 官网下载 OnlyOffice Desktop&#xff0c;安装过程很简单&#xff0c;一路下一步即可。用 OnlyOffice 制作 PDF 模版&#xff08;表单&#xff09; 使用 OnlyOffice 表单设计器&#xff0c;制作表单&#xff0c;如下图 注意命名…

使用安装 Kettle 教程 Pentoho 10.2.0.0-222 安装 连接mysql

流程 准备下载安装测试链接常见问题 准备 需要提前安装好 JDK 配置好环境变量 &#xff08;教程看前文&#xff09; 安装好mysql&#xff08;教程看前文&#xff09; 下载好pentaho链接数据库驱动 下载pentaho安装包 https://pentaho.com/wp-content/uploads/2024/04/three-s…