建网站需要营业执照吗,网站建设需要集齐哪5份资料,php网站开发进程,本地wordpress后台什么是Emma EMMA 是一个开源、面向 Java 程序测试覆盖率收集和报告工具。 它通过对编译后的 Java 字节码文件进行插装#xff0c;在测试执行过程中收集覆盖率信息#xff0c;并通过支持多种报表格式对覆盖率结果进行展示。 EMMA 所使用的字节码插装不仅保证 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插件。 plugingroupIdorg.apache.maven.plugins/groupIdartifactIdmaven-surefire-plugin/artifactIdversion2.8.1/versionconfigurationskipTestsfalse/skipTestsjunitArtifactNamejunit:junit/junitArtifactNameincludesinclude**/*Test.java/include/includesexcludesexclude**/*_Roo_*/exclude/excludes/configuration
/plugin这样指定 maven-surefire-plugin 的版本为2.8.1skipTestsfalse/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即可使用。 因为测试需要很长时间而package命令会自动执行测试所以有时候我们不想所有项目都测试。可以使用如下方案系统配置两个分支A分支用于开发B分支用于上线。我们希望只要A分支进行emma测试而在B分支不用测试方便快速上线。配置如下 在项目的pom.xml中skipTestsfalse/skipTests默认不跳过测试在B项目中配置 clean -U compile package -Dmaven.test.skiptrue 用来跳过测试。 查看测试报告 本地测试查看 生成的报告是以html存储默认的位置是${项目目录}/target/site/emma,打开index.html可以查看。 里面有类覆盖率方法覆盖率块覆盖率行覆盖率 选中其中的java文件还可以查看具体的代码覆盖率 绿色为有测试的红色的是测试未覆盖的。 Jenkins 测试查看 在项目主页中查看 这里会有项目的测试覆盖率曲线。x轴是版本变化y轴是测试覆盖率。 点进图片进入本版本的详细测试报告。具体的形式和本地测试报告差不多只是 jenkins测试报告没有具体的代码测试详情。 工作原理 emma现在有两种工作方式on-the-fly模式和offline模式 On the fly 模式往加载的类中加入字节码在程序运行中用 EMMA 实现的classLoader 替代应用默认的 Custom classLoader,动态加载类文件并向类中加入一些统计测试的字节码这样运行结束后测试数据也就通过这些临时加入的字节码分析出来。 Offline 模式在类被加载前在编译生成的class文件中加入字节码。 On the fly 模式比较方便缺点也比较明显 它不能为被 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工作时主要运行以下几个步骤 字节码插装并生成插装的元信息文件coverage.em运行测试每次当 JVM 停止时内存中记录的执行信息将被清除并被保存到 “coverage.ec” 的文件中。生成测试报告。插装字节码 emma执行是最重要的就是插装字节码 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 ;i10;i){privateTest();if(i4){continue;}if(i3){break;}//永远不会执行到这一步所以protectedTest()并没有被覆盖if(i5){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// isnt 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能够检测出那些虽然调用但没有执行到的代码。 收集覆盖率信息 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 RTs 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.4throw 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 ]);}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测试覆盖率工具