Cobertura原理

原文链接:https://blog.csdn.net/kittyboy0001/article/details/25078449

Cobertura原理

Cobertura 是一种开源工具,它通过检测基本的代码,并观察在测试包运行时执行了哪些代码和没有执行哪些代码,来测量测试覆盖率。

官方地址:http://sourceforge.net/projects/cobertura/
日本下载:http://sourceforge.jp/projects/sfnet_cobertura/releases/

Cobertura 在 Maven 编译平台上有相应的 cobertura-maven-plugin 插件,使代码编译、检测、集成等各个周期可以流水线式自动化完成。

插件地址:http://mojo.codehaus.org/cobertura-maven-plugin/

Cobertura原理及过程

cobertura执行过程大致如下:
1,使用instrument修改我们编译后的class文件。
2,执行测试,测试数据输出到xxx.ser中。
3,使用report生成覆盖率报告。

1、instrument

cobertura使用instrument修改我们编译后的class文件,在代码里面加入cobertura的统计代码。并生成一个.ser文件(用于覆盖率数据的输出)。

在使用instrument执行的过程中,CoberturaInstrumenter会首先调用分析监听器分析给定的编译好的.class,获得touchPoint(可以认为对应于源代码中的待覆盖行)以及需要的其他信息。然后调用注入监听器将信息注入到新的.class中,保存到\target\generated-classes目录下。

demo的源代码如下(裁剪并只保留留下类的代码):

public class FeeCodeTranslate {
    private static final Map<String, String> FEECODEMAP = new HashMap<String, String>();
    static{
        FeeCodeTranslate.FEECODEMAP.put("机架费用合计", "JJFY");
        FeeCodeTranslate.FEECODEMAP.put("带宽费用合计", "DKFY");
        FeeCodeTranslate.FEECODEMAP.put("光纤租用费用", "CSFY");
    }
    public static String nameToCode(String name){
        String code = FeeCodeTranslate.FEECODEMAP.get(name);
        return code == null ? "" : code;
    }
}

源代码生成.class文件后,反编译如下:

public class FeeCodeTranslate {
    private static final Map FEECODEMAP;
    public FeeCodeTranslate() //增加了构造函数
    {
    }
    public static String nameToCode(String name){
        String code = (String)FEECODEMAP.get(name);
        return code != null ? code : "";
    }
    static {
        FEECODEMAP = new HashMap();
        FEECODEMAP.put("机架费用合计", "JJFY");
        FEECODEMAP.put("带宽费用合计", "DKFY");
        FEECODEMAP.put("光纤租用费用", "CSFY");
    }
}

使用instrument执行后,得到新的.class如下:

public class FeeCodeTranslate {
 
    public static final transient int __cobertura_counters[]; //增加了计数数组
    private static final Map FEECODEMAP;
 
    public FeeCodeTranslate()
    {
        int i = 0;
        __cobertura_counters[1]++; //增加了计数代码
        super();
    }
 
    public static String nameToCode(String name){
        String code;
        __cobertura_init();     //增加了计数代码,初始化计数代码
        int i = 0;
        __cobertura_counters[7]++; //增加了计数代码
        code = (String)FEECODEMAP.get(name);
        __cobertura_counters[8]++; //增加了计数代码
        code;
        int j = 10;
        JVM INSTR ifnonnull 67;
           goto _L1 _L2
_L1:
        __cobertura_counters[9]++;  //增加了计数代码
        j = 0;
        "";
          goto _L3
_L2:
        __cobertura_counters[j]++;  //增加了计数代码
        j = 0;
        code;
_L3:
        return;
    }
 
    public static void __cobertura_init() { //增加了计数代码
        if (__cobertura_counters == null) {
            __cobertura_counters = new int[11];
            TouchCollector.registerClass("com/baidu/idc/base/FeeCodeTranslate");
        }
    }
    //增加了计数代码
    public static void __cobertura_classmap_0(LightClassmapListener  lightclassmaplistener) {
        lightclassmaplistener.putLineTouchPoint(11, 1, "<init>", "()V");
        lightclassmaplistener.putLineTouchPoint(13, 2, "<clinit>", "()V");
        lightclassmaplistener.putLineTouchPoint(15, 3, "<clinit>", "()V");
        lightclassmaplistener.putLineTouchPoint(16, 4, "<clinit>", "()V");
        lightclassmaplistener.putLineTouchPoint(17, 5, "<clinit>", "()V");
        lightclassmaplistener.putLineTouchPoint(18, 6, "<clinit>", "()V");
        lightclassmaplistener.putLineTouchPoint(21, 7, "nameToCode", "(Ljava/lang/String;)Ljava/lang/String;");
        lightclassmaplistener.putLineTouchPoint(22, 8, "nameToCode", "(Ljava/lang/String;)Ljava/lang/String;");
        lightclassmaplistener.putJumpTouchPoint(22, 10, 9);
        LightClassmapListener  = lightclassmaplistener;
    }
    //增加了计数代码
    public static void __cobertura_classmap( LightClassmapListener lightclassmaplistener) {
        lightclassmaplistener.setClazz("com/baidu/idc/base/FeeCodeTranslate");
        lightclassmaplistener.setSource("FeeCodeTranslate.java");
        __cobertura_classmap_0(lightclassmaplistener);
        LightClassmapListener  = lightclassmaplistener;
    }
    //增加了计数代码
    public static int[] __cobertura_get_and_reset_counters(){
        int ai[] = __cobertura_counters;
        __cobertura_counters = new int[__cobertura_counters.length];
        return ai;
    }
    static {
        __cobertura_init(); //增加了计数代码
        int i = 0;
        __cobertura_counters[2]++; //增加了计数代码
        FEECODEMAP = new HashMap();
        __cobertura_counters[3]++; //增加了计数代码
        FEECODEMAP.put("机架费用合计", "JJFY");
        __cobertura_counters[4]++; //增加了计数代码
        FEECODEMAP.put("带宽费用合计", "DKFY");
        __cobertura_counters[5]++; //增加了计数代码
        FEECODEMAP.put("光纤租用费用", "CSFY");
        __cobertura_counters[6]++; //增加了计数代码
    }
}
2、执行测试

为了在测试用例执行时,调用cobertura修改过的.class命令。classpath设置时,instrumented classes 要在original (uninstrumented) classes前面。这样测试时就会引用cobertura修改过的.class。测试信息写入到cobertura.ser档案文件。

命令执行时需要注意的事项,参见如下ant中的片段:

    <target name="test" depends="init,compile">
        <junit fork="yes" dir="${basedir}" failureProperty="test.failed">
            <!--
                Specify the name of the coverage data file to use.
            -->
            <sysproperty key="net.sourceforge.cobertura.datafile" file="${reports.dir}/coverage.ser" />
 
            <!--
                Note the classpath order: instrumented classes are before the
                original (uninstrumented) classes.  This is important.
            -->
            <classpath location="${instrumented.dir}" />
            <classpath location="${classes.dir}" />
 
            <!--
                The instrumented classes reference classes used by the
                Cobertura runtime, so Cobertura and its dependencies
                must be on your classpath.
            -->
            <classpath refid="cobertura_classpath" />
 
            <formatter type="xml" />
            <test name="${testcase}" todir="${reports.xml.dir}" if="testcase" />
            <batchtest todir="${reports.xml.dir}" unless="testcase">
                <fileset dir="${src.dir}">
                    <include name="**/*Test.java" />
                </fileset>
            </batchtest>
        </junit>
 
        <junitreport todir="${reports.xml.dir}">
            <fileset dir="${reports.xml.dir}">
                <include name="TEST-*.xml" />
            </fileset>
            <report format="frames" todir="${reports.html.dir}" />
        </junitreport>
    </target>
3、生成报告

从cobertura.ser获取覆盖率数据,然后结合src中提供的源代码,生成最终的覆盖率报告,放到了target\site\cobertura路径下。

java -cp "%COBERTURA_HOME%cobertura.jar;..." 
net.sourceforge.cobertura.reporting.Main --format html 
--datafile cobertura.ser --destination cobertura-site src

最终,得到覆盖率报告数据如下图所示:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值