Emma使用与分析

什么是Emma

EMMA 是一个开源、面向 Java 程序测试覆盖率收集和报告工具。

它通过对编译后的 Java 字节码文件进行插装,在测试执行过程中收集覆盖率信息,并通过支持多种报表格式对覆盖率结果进行展示。 EMMA 所使用的字节码插装不仅保证 EMMA 不会给源代码带来“脏代码”,还确保 EMMA 摆脱了源代码的束缚,这一特点使 EMMA 应用于功能测试成为了可能。

如何使用

emma现在可以通过命令行,ant,maven,Jenkins等方式使用,这里只介绍通过maven和Jenkins来集成emma测试。

在Maven中的使用

直接运行mvn emma:emma,即可。

maven集成emma,需要两个插件,maven-surefire-plugin和emma-maven-plugin,如果之前没有安装,那么maven会自动下载这两个插件。

emma依赖于surefire的配置,默认执行src/test/java的junit测试。为了方便使用,最好在自己的pom里配置maven-surefire-plugin插件。

<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>2.8.1</version><configuration><skipTests>false</skipTests><junitArtifactName>junit:junit</junitArtifactName><includes><include>**/*Test.java</include></includes><excludes><exclude>**/*_Roo_*</exclude></excludes></configuration>
</plugin>

这样指定 maven-surefire-plugin 的版本为2.8.1,<skipTests>false</skipTests>不跳过测试,<include>**/*Test.java</include> 只测试以 Test.java 为文件名结尾的文件,而且不测试<exclude>**/*_Roo_*</exclude> 文件名包含_Roo_的文件。

更多的配置可以去查看maven-surefire-plugin的配置说明

  • http://maven.apache.org/plugins/maven-surefire-plugin/。

在Jenkins中的使用

在Jenkins系统管理的插件管理页面,添加Jenkins Emma plugin插件。

在项目配置中,加入emma:emma即可使用。

emma_1

emma_2

因为测试需要很长时间,而package命令会自动执行测试,所以有时候我们不想所有项目都测试。可以使用如下方案:系统配置两个分支,A分支用于开发,B分支用于上线。我们希望只要A分支进行emma测试,而在B分支不用测试方便快速上线。配置如下

在项目的pom.xml中,<skipTests>false</skipTests>默认不跳过测试,在B项目中配置 clean -U compile package -Dmaven.test.skip=true ,用来跳过测试。

查看测试报告

本地测试查看:

生成的报告是以html存储,默认的位置是${项目目录}/target/site/emma,打开index.html可以查看。

里面有类覆盖率,方法覆盖率,块覆盖率,行覆盖率,

emma_3

选中其中的java文件还可以查看具体的代码覆盖率

emma_4

绿色为有测试的,红色的是测试未覆盖的。

Jenkins 测试查看:

在项目主页中查看

emma_5

这里会有项目的测试覆盖率曲线。x轴是版本变化,y轴是测试覆盖率。

点进图片进入本版本的详细测试报告。具体的形式和本地测试报告差不多,只是 jenkins测试报告没有具体的代码测试详情。

工作原理

emma现在有两种工作方式,on-the-fly模式和offline模式:

emma_6

On the fly 模式往加载的类中加入字节码,在程序运行中,用 EMMA 实现的classLoader 替代应用默认的 Custom classLoader,动态加载类文件,并向类中加入一些统计测试的字节码,这样运行结束后,测试数据也就通过这些临时加入的字节码分析出来。

Offline 模式在类被加载前,在编译生成的class文件中加入字节码。

On the fly 模式比较方便,缺点也比较明显:

emma_11

它不能为被 boot class loader 加载的类生成覆盖率报告;而且,J2EE的classLoader和EMMA的classLoader都是同一类Custom classLoader,在j2ee项目启动过程中,必须选择应用容器(tomcat、Weblogic等等)相应的classLoader,从而无法使用emma的classLoader。同时,jenkins必须配合mvn的框架才能运行emma相关命令,而mvn框架只支持offline模式,所以如果想使用jenkins来做测试报告的话,就无法使用on the fly模式。

在官方文档里也有说明:

As convenient as the on-the-fly mode is, in many cases it is not sufficient. For example, running a commercial J2EE container in a custom instrumenting classloader is practically impossible. Certain (bad) coding practices also fail for code executing in a custom classloader. This on-the-fly instrumentation mode is handy for light-weight testing of main() test methods, individual classes, and small- to- mid-size programs. emmarun also works well with Swing applications.

这时,我们只能求助于 Offline 模式。下面用maven的运行方式来介绍一下。

通过在maven中执行,我们可以看出emma工作时主要运行以下几个步骤

emma_7

  1. 字节码插装并生成插装的元信息文件coverage.em
  2. 运行测试
  3. 每次当 JVM 停止时,内存中记录的执行信息将被清除并被保存到 “coverage.ec” 的文件中。
  4. 生成测试报告。

插装字节码

emma执行是最重要的就是插装字节码:

emma_8

emma循环调用handleFile()方法来遍历目录下所有以’.class’结尾的文件,然后使用 classParser类得到要插装的组件

ClassDef class_table () throws IOException {m_table = new ClassDef ();magic ();version ();if (DEBUG) System.out.println (s_line);constant_pool ();if (DEBUG) System.out.println (s_line);access_flags ();this_class ();super_class ();if (DEBUG) System.out.println (s_line);//得到所有接口interfaces ();if (DEBUG) System.out.println (s_line);//得到所有字段fields ();if (DEBUG) System.out.println (s_line);//得到所有方法methods ();if (DEBUG) System.out.println (s_line);//得到所有attributeattributes ();if (DEBUG) System.out.println (s_line);return m_tabl e;
}

offline模式的插装会生成全新的class文件,默认放在target/generated-classes下。以下是原java文件和插装后的class反编译的java文件。

public class EmmaMain2 {private Logger logger = LoggerFactory.getLogger(this.getClass());//junit调用publicTest()进行测试public void publicTest(){logger.info("this is a public method");logger.info("我是分隔符------------------------------------------------------------------");for(int i =1 ;i<10;i++){privateTest();if(i==4){continue;}if(i==3){break;}//永远不会执行到这一步,所以protectedTest()并没有被覆盖if(i==5){protectedTest();return;}}}protected void protectedTest(){logger.info("this is a protected method");}private void privateTest(){logger.info("this is a private method");}
}

插装字节码之后反编译的代码

public class EmmaMain2
{private Logger logger = LoggerFactory.getLogger(getClass());private static final boolean[][] $VRc;private static final long serialVersionUID = -6204774612524021426L;public EmmaMain2(){arrayOfBoolean[0] = true;}public void publicTest(){boolean[][] tmp3_0 = $VRc; if (tmp3_0 == null) tmp3_0; boolean[] arrayOfBoolean = $VRi()[1]; this.logger.info("this is a public method");this.logger.info("我是分隔符------------------------------------------------------------------");int i = 1; arrayOfBoolean[0] = true;tmpTernaryOp = tmp3_0;do{privateTest();arrayOfBoolean[1] = true; if (i == 4) { arrayOfBoolean[2] = true;} else{arrayOfBoolean[3] = true; if (i == 3) { arrayOfBoolean[4] = true;break;}arrayOfBoolean[5] = true; if (i == 5) {protectedTest(); arrayOfBoolean[6] = true;return;}}i++; arrayOfBoolean[7] = true; arrayOfBoolean[8] = true; } while (i < 10);arrayOfBoolean[9] = true;}protected void protectedTest(){boolean[][] tmp3_0 = $VRc; if (tmp3_0 == null) tmp3_0; boolean[] arrayOfBoolean = $VRi()[2]; this.logger.info("this is a protected method");arrayOfBoolean[0] = true;}private void privateTest(){boolean[][] tmp3_0 = $VRc; if (tmp3_0 == null) tmp3_0; boolean[] arrayOfBoolean = $VRi()[3]; this.logger.info("this is a private method");arrayOfBoolean[0] = true;}static{boolean[] arrayOfBoolean = $VRi()[4];arrayOfBoolean[0] = true;}private static boolean[][] $VRi(){boolean[][] tmp9_6 = (EmmaMain2.$VRc = new boolean[5]);tmp9_6[0] = new boolean[1];boolean[][] tmp15_9 = tmp9_6;tmp15_9[1] = new boolean[10];boolean[][] tmp22_15 = tmp15_9;tmp22_15[2] = new boolean[1];boolean[][] tmp28_22 = tmp22_15;tmp28_22[3] = new boolean[1];boolean[][] tmp34_28 = tmp28_22;tmp34_28[4] = new boolean[1];boolean[][] tmp40_34 = tmp34_28;//将类信息加载到内存中。RT.r(tmp40_34, "com/impulse/test/emma/EmmaMain2", -5598510326399570528L);return tmp40_34;}
}

反编译有些问题,但我们可以看出,emma在每个方法的入口和出口和转移指令之前如return、break、continue都加入了监测代码,并在最后把代码的执行情况通过RT.r()方法加载到内存的m_coverageMap中。

public static void r (final boolean [][] coverage, final String classVMName, final long stamp){// note that we use class names, not the actual Class objects, as the keys here. This// is not the best possible solution because it is not capable of supporting// multiply (re)loaded classes within the same app, but the rest of the toolkit// isn't designed to support this anyway. Furthermore, this does not interfere// with class unloading.final ICoverageData cdata = getCoverageData (); // need to use accessor for JMM reasons// ['cdata' can be null if a previous call to dumpCoverageData() disabled data collection]if (cdata != null){synchronized (cdata.lock ()){// TODO: could something useful be communicated back to the class// by returning something here [e.g., unique class ID (solves the// issues of class name collisions and class reloading) or RT.class// (to prevent RT reloading)]cdata.addClass (coverage, classVMName, stamp);}}}public void addClass (final boolean [][] coverage, final String classVMName, final long stamp){m_coverageMap.put (classVMName, new DataHolder (coverage, stamp));}

所以当我们只测试publicTest()时,虽然publicTest()调用了protectedTest(),但由于我们通过条件语句的控制,使得protectedTest()永远不会被执行,因此在转移指令时加监控是必要的,我们可以在生成的报告中看出,

emma_9

emma能够检测出那些虽然调用但没有执行到的代码。

收集覆盖率信息

emma会检测jvm的运行情况,当通过命令行调用reset或者虚拟机停止(一般是测试完成时),emma会将测试的覆盖率信息通过 dumpCoverageData()方法导出成实体文件。默认为coverage-*.ec文件。

static void dumpCoverageData (final ICoverageData cdata, final boolean useSnapshot,final File outFile, final boolean merge){try{if (cdata != null){// use method-scoped loggers everywhere in RT:final Logger log = Logger.getLogger ();final boolean info = log.atINFO ();final long start = info ? System.currentTimeMillis () : 0;{final ICoverageData cdataView = useSnapshot ? cdata.shallowCopy () : cdata;synchronized (Object.class) // fake a JVM-global critical section when multilply loaded RT's write to the same file{//在这里生者覆盖率信息文件,cdataView是CoverageData型,有一个重要的成员变量就是上面说的m_coverageMapDataFactory.persist (cdataView, outFile, merge);}}if (info){final long end = System.currentTimeMillis ();log.info ("runtime coverage data " + (merge ? "merged into" : "written to") + " [" + outFile.getAbsolutePath () + "] {in " + (end - start) + " ms}");}}}catch (Throwable t){// logt.printStackTrace ();// TODO: do better chaining in JRE 1.4+throw new RuntimeException (IAppConstants.APP_NAME + " failed to dump coverage data: " + t.toString ());}}

DataFactory.persist (cdataView, outFile, merge); cdataView是CoverageData型,有一个重要的成员变量就是上面说的m_coverageMap,没错,就是在这里把存在内存中的测试覆盖率信息持久化到文件中。

生成测试报告

AbstractReportGenerator是个抽象工厂,根据参数不同而产生不同的 ReportGenerator。

public static IReportGenerator create (final String type){if ((type == null) || (type.length () == 0))throw new IllegalArgumentException ("null/empty input: type");// TODO: proper pluggability pattern hereif ("html".equals (type))return new com.vladium.emma.report.html.ReportGenerator ();else if ("txt".equals (type))return new com.vladium.emma.report.txt.ReportGenerator ();else if ("xml".equals (type))return new com.vladium.emma.report.xml.ReportGenerator ();else // TODO: error codethrow new EMMARuntimeException ("no report generator class found for type [" + type + "]");}

emma_10

public
abstract class AbstractItemVisitor implements IItemVisitor
{// public: ................................................................//概要覆盖信息public Object visit (final AllItem item, final Object ctx){return ctx;}//包测试覆盖信息public Object visit (final PackageItem item, final Object ctx){return ctx;}//源文件测试覆盖信息public Object visit (final SrcFileItem item, final Object ctx){return ctx;}//在html中没有public Object visit (final ClassItem item, final Object ctx){return ctx;}//在html没有public Object visit (final MethodItem item, final Object ctx){return ctx;}
}

三种ReportGenerator都实现了IReportGenerator接口的process方法来到处报告,而process方法又分别调用了各种重载的visit()方法。当maven生成html测试报告是,只用了生成概要覆盖信息、源文件测试覆盖信息、包测试覆盖信息的方法。

参考资料

  • emma官方网站
  • 使用 EMMA 测量测试覆盖率
  • 可爱的EMMA:测试覆盖率工具

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

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

相关文章

论文浅尝 | WWW2022 - “知识提示”之知识约束预训练微调

本文作者 | 陈想&#xff08;浙江大学&#xff09;、张宁豫&#xff08;浙江大学&#xff09;、谢辛&#xff08;陈想&#xff09;、邓淑敏&#xff08;浙江大学&#xff09;姚云志&#xff08;浙江大学&#xff09;、谭传奇&#xff08;阿里巴巴&#xff09;&#xff0c;黄非&…

吐血整理:论文写作中注意这些细节,能显著提升成稿质量

文 | python编 | 小轶前言不知诸位在科研的起步阶段&#xff0c;是否曾有过如下的感受&#xff1a;总感觉自己写的论文就是和自己读过的论文长得不太一样&#xff0c;也不知道为啥。投稿的时候&#xff0c;审稿人也总是 get 不到论文的核心&#xff0c;只揪着论文的次要细节不放…

LeetCode 1237. 找出给定方程的正整数解

1. 题目 给出一个函数 f(x, y) 和一个目标结果 z&#xff0c;请你计算方程 f(x,y) z 所有可能的正整数 数对 x 和 y。 给定函数是严格单调的&#xff0c;也就是说&#xff1a; f(x, y) < f(x 1, y) f(x, y) < f(x, y 1)函数接口定义如下&#xff1a; interface Cu…

基于Wi-Fi的室内定位在美团总部的实践和应用(上)

室内定位技术的商业化必将带来一波创新高潮&#xff0c;尤其是在O2O领域&#xff0c;各种基于此技术的应用将出现在我们的面前。我们可以想象一些比较常见的应用场景&#xff0c;比如在大型商场里面借助室内导航快速找到目标商铺&#xff0c;商店根据用户的具体位置向用户推送更…

LeetCode 1266. 访问所有点的最小时间(数学)

1. 题目 平面上有 n 个点&#xff0c;点的位置用整数坐标表示 points[i] [xi, yi]。请你计算访问所有这些点需要的最小时间&#xff08;以秒为单位&#xff09;。 你可以按照下面的规则在平面上移动&#xff1a; 每一秒沿水平或者竖直方向移动一个单位长度&#xff0c;或者…

Coding Party 邀你出战!飞桨黑客马拉松线下场来啦

12月4-5日&#xff0c;这48H&#xff0c;与100开发者HACK Together“黑客”很酷&#xff1f;和“黑客”一起“战斗”是种什么样的体验&#xff1f;你参与这场“战斗”了吗&#xff1f;PaddlePaddle Hackathon 2021飞桨黑客马拉松&#xff0c;由百度飞桨联合深度学习技术及应用国…

技术动态 | 「知识图谱嵌入技术研究」最新2022综述

转载公众号 | 专知知识图谱(KG)是一种用图模型来描述知识和建模事物之间关联关系的技术. 知识图谱嵌入(KGE)作为一 种被广泛采用的知识表示方法,其主要思想是将知识图谱中的实体和关系嵌入到连续的向量空间中,用来简化操作, 同时保留 KG 的固有结构.它可以使得多种下游任务受益…

美团数据仓库-数据脱敏

背景与目标 在数据仓库建设过程中&#xff0c;数据安全扮演着重要角色&#xff0c;因为隐私或敏感数据的泄露&#xff0c;会对数据主体&#xff08;客户&#xff0c;员工和公司&#xff09;的财产、名誉、人身安全、以及合法利益造成严重损害。因此我们需要严格控制对仓库中的数…

全球仅3000人通过的TensorFlow开发人员认证到底有多香!

现在&#xff0c;我们每个人都已经习惯了社交网络&#xff0c;随时随地都能进行微信、QQ语音和视频通话。那么&#xff0c;你是否发现在不同场景下&#xff0c;这些社交软件音频、视频都是那么的流畅&#xff0c;这背后是什么科技在支撑这“声”与“话”的美好呢&#xff1f;这…

LeetCode 1043. 分隔数组以得到最大和(DP)

1. 题目 给出整数数组 A&#xff0c;将该数组分隔为长度最多为 K 的几个&#xff08;连续&#xff09;子数组。分隔完成后&#xff0c;每个子数组的中的值都会变为该子数组中的最大值。 返回给定数组完成分隔后的最大和。 示例&#xff1a; 输入&#xff1a;A [1,15,7,9,2,…

图谱实战 | 安全领域知识图谱建设与典型应用场景总结

转载公众号 | 老刘说NLP 安全知识图谱是网络安全领域专用知识图谱&#xff0c;也是知识图谱应用于安全业务的重要工业尝试。当前&#xff0c;安全领域中存在大量的业务数据&#xff0c;建模需求以及应用需求&#xff0c;了解安全领域知识图谱的建设方法以及典型应用场景&#x…

Hadoop安全实践

前言 在2014年初&#xff0c;我们将线上使用的 Hadoop 1.0 集群切换到 Hadoop 2.2.0 稳定版&#xff0c; 与此同时部署了 Hadoop 的安全认证。本文主要介绍在 Hadoop 2.2.0 上部署安全认证的方案调研实施以及相应的解决方法。 背景 集群安全措施相对薄弱 最早部署Hadoop集群时并…

论文浅尝 | KGQR: 用于交互式推荐的知识图谱增强Q-learning框架

笔记整理&#xff1a;李爽&#xff0c;天津大学链接&#xff1a;https://dl.acm.org/doi/pdf/10.1145/3397271.3401174动机交互式推荐系统(IRS)以其灵活的推荐策略和考虑最佳的长期用户体验而备受关注。为了处理动态用户偏好&#xff0c;研究人员将强化学习(reinforcement lear…

收藏清单:史上最全java自动化测试工具汇总

xUnit frameworks 单元测试框架 JUnit - 常用的测试框架TestNG - 测试框架 Test Data Supplier - TestNG DataProvider封装&#xff0c;让数据的生成更加灵活 TDD \ ATDD \ BDD 工具 JBehave - Behaviour-Driven Development (BDD)测试框架. BDD是从 test-driven development…

LeetCode 1014. 最佳观光组合

1. 题目 给定正整数数组 A&#xff0c;A[i] 表示第 i 个观光景点的评分&#xff0c;并且两个景点 i 和 j 之间的距离为 j - i。 一对景点&#xff08;i < j&#xff09;组成的观光组合的得分为&#xff08;A[i] A[j] i - j&#xff09;&#xff1a;景点的评分之和减去它…

聊聊推荐系统的高阶特征交叉问题

文 | 水哥源 | 知乎Saying1. DCN看起来给了我们很好的允诺&#xff0c;但是细细想来是有一些问题的&#xff0c;这里也可以参考大佬的意见2. 高阶FM的核心设计是先element-wise乘&#xff0c;再对embedding求和。核心优化方法是利用计算中的冗余构建递推关系&#xff0c;然后使…

COS系统的前端演变和发展

背景 美团COS&#xff1a;全称美团网核心业务系统部&#xff0c;以持续整合O2O线下资源&#xff0c;共建高效率、低成本的供应链系统&#xff0c;高效推动O2O生态环境建设为业务目标&#xff0c;负责美团网核心业务系统的建设和管理。 COS系统&#xff0c;伴随着美团3年多的发展…

OpenKG 祝大家元宵节快乐!

OpenKGOpenKG&#xff08;中文开放知识图谱&#xff09;旨在推动以中文为核心的知识图谱数据的开放、互联及众包&#xff0c;并促进知识图谱算法、工具及平台的开源开放。点击阅读原文&#xff0c;进入 OpenKG 网站。

LeetCode 1262. 可被三整除的最大和(DP)

1. 题目 给你一个整数数组 nums&#xff0c;请你找出并返回能被三整除的元素最大和。 示例 1&#xff1a; 输入&#xff1a;nums [3,6,5,1,8] 输出&#xff1a;18 解释&#xff1a;选出数字 3, 6, 1 和 8&#xff0c;它们的和是 18&#xff08;可被 3 整除的最大和&#xff…

iBeacon 初探

iBeacon 是苹果公司在 iOS 7 中新推出的一种近场定位技术&#xff0c;可以感知一个附近的 iBeacon 信标的存在。 当一个 iBeacon 兼容设备进入/退出一个 iBeacon 信标标识的区域时&#xff0c;iOS 和支持 iBeacon 的 app 就能得知这一信息&#xff0c;从而对用户发出相应的通知…