利用Java反射(Reflection) 机制提高代码的行覆盖率

在本文中,您将看到如何通过使用反射机制,在外部直接对目标类中的不可访问成员进行测试,以提高被测代码数量;以及通过修改 Cobertura 源码,使其支持通过正则表达式来过滤不需要进行单元测试的代码,以降低代码总数。代码覆盖率的提高,减少了单元测试过程中未被覆盖的代码数量,降低了">开发人员编写或修改单元测试用例的时间成本,从而提高了整个单元测试的效率。

单元测试是软件开发过程中重要的质量保证环节。单元测试可以减少代码中潜在的错误,使缺陷更早地被发现,从而降低了软件的维护成本。软件代码的质量由单元测试来保证,而单元测试自身的质量与效率问题也不容忽视。提高单元测试的质量与效率,不仅能够使软件代码更加有保证,而且能够节省开发人员编写或者修改单元测试代码的时间。衡量单元测试质量与效率的指标多种多样,代码覆盖率是其中一个极为重要的指标。一般而言,代码覆盖率越高,单元测试覆盖的范围就越大,代码中潜在错误的数量就越少,软件质量就越高。本文首先介绍代码覆盖率的统计指标类型及常用统计工具,然后重点选取具有代表性的行覆盖率进行分析,介绍两种方法用于提高代码的行覆盖率。

代码覆盖率的统计指标

代码覆盖率指的是一种衡量代码覆盖程度的方式,通常会对以下几种方式进行统计分析:

行覆盖。它又被称作语句覆盖或基本块覆盖。这是一种较为常用且具有代表性的指标,度量的是被测代码中每个可执行语句是否被执行到。 条件覆盖。它度量的是当代码中存在分支时,是否能覆盖进入分支和不进入分支这两种情况。这要求开发人员编写多个测试用例以分别满足进入分支与不进入分支这两种情况。 路径覆盖。它度量的是当代码中存在多个分支时,是否覆盖到分支之间不同组合方式所产生的全部路径。这是一种力度最强的覆盖检测,相对而言,条件覆盖只是路径覆盖中的一部分。

在这三种覆盖指标中,行覆盖简单,适用性广,但可能会被认为是“最弱的覆盖”,其实不然。行覆盖相对于条件或路径覆盖,可以使开发人员通过尽可能少的测试数据和用例,覆盖尽可能多的代码。通常情况下,是先通过工具检测一遍整个工程单元测试的行覆盖情况,然后针对没有被覆盖到的代码,分析其没有被覆盖到的原因。如果是由于该代码所在分支由于不满足进入该分支的条件而没有被覆盖,那么开发人员才会进一步修改或增加测试代码,完成该部分的条件或路径覆盖。

可见,高效高质量的行覆盖是有效进行条件覆盖与路径覆盖的前提。行覆盖率越高,说明没有被覆盖到的代码越少,这样开发人员便会集中精力修改测试用例,覆盖这些数量不多的代码。相反,如果行覆盖率低,开发人员需要逐个检查没有被覆盖到的代码,精力被分散,因此很难提高剩余代码单元测试的质量。

代码覆盖率 = 被测代码行数 / 参测代码总行数 * 100%。 从代码覆盖率的计算方式中可以看出,要提高代码覆盖率,可通过提高被测代码行数,或减少参测代码总行数的方式进行。以下将会从这两个角度分别入手,分析如何提高被测代码行数及减少参测代码总行数。

使用 Cobertura 统计并提高代码的行覆盖率

Cobertura 是一款优秀的开源测试覆盖率统计工具,它与单元测试代码结合,标记并分析在测试包运行时执行了哪些代码和没有执行哪些代码以及所经过的条件分支,来测量测试覆盖率。除了找出未测试到的代码并发现 bug 外,Cobertura 还可以通过标记无用的、执行不到的代码来优化代码,最终生成一份美观详尽的 HTML 覆盖率检测报告。

Cobertura 基本工具包里有四个基本过程及对应的工具:cobertura-check, cobertura-instrument, cobertura-merge, cobertura-report; 这个脚本独立使用较为繁琐,不方便也不利于自动化。不过, Cobertura 在 Maven 编译平台上有相应的 cobertura-maven-plugin 插件,使代码编译、检测、集成等各个周期可以流水线式自动化完成。

Cobertura-maven-plugin 官方版有五个主要目标指令 (goal),如表 1:

表 1. Cobertura 目标指令及作用解释

目标指令 作用解释 Cobertura:check 检查最后一次标注(instrumentation) 正确与否 Cobertura:clean 清理插件生产的中间及最终报告文件 Cobertura:dump-datafile Cobertura 数据文件 dump 指令 , 不常用 Cobertura:instrument 标注编译好的 javaclass 文件 Cobertura:cobertura 标注、运行测试并产生 Cobertura 覆盖率报告

Cobertura 通常会与 Maven 一起使用。因此工程目录结构如果遵循 Maven 推荐的标准的话,一个集成 Cobertura 的基本 POM 文件如清单 1 所示:

清单 1. POM 文件的基本结构

<project> <reporting> <plugins> <plugin> <!-- 此处用于将 Cobertura 插件集成到 Maven 中 --> <groupId>org.codehaus.mojo</groupId> <artifactId>cobertura-maven-plugin</artifactId> <version>2.5.2</version> </plugin> </plugins> </reporting> </project>

如果工程目录结构没有采用 Maven 推荐标准,则需要进行如下额外设置:

清单 2. 适合 Maven 的工程目录结构配置

<build> <!-- Java 源代码的路径配置 --> <sourceDirectory>src/main/java</sourceDirectory> <scriptSourceDirectory>src/main/scripts</scriptSourceDirectory> <!-- 测试代码的路径配置 --> <testSourceDirectory>src/test/java</testSourceDirectory> <!-- 源码编译后的 class 文件的路径配置 --> <outputDirectory>target/classes</outputDirectory> <!-- 测试源码编译后的 class 文件的路径配置 --> <testOutputDirectory>target/test-classes</testOutputDirectory> <plugin> .... </plugin> </build>

单元测试代码编写完成,所有设置配制好后,在工程根目录运行“mvn cobertura:cobertura”Maven 就会对代码进行编译。编译完成之后,就会在项目中运行测试代码并输出测试报告结果到目录 project_base$\target\site\cobertura\index.html,效果如图 1 所示。

图 1. Cobertura 覆盖分析报告

从以上报告中可见,

代码
整体的行覆盖率并不高,有些包或类覆盖率很低,甚至为 0。考虑到这些包或类的特殊性(
例如它们已被其他类取代),无需对它们进行单元测试,因此需要从整个测试范围中剔除。 部分类的行覆盖率虽然已接近 100%,但仍存在一些方法(如 set 和 get 方法)由于没有测试的必要却被列入了统计范围,这些方法需要被过滤掉。

针对上述两种改进措施,都可以使用 Cobertura 进行实现。第一种改进措施 Cobertura 可以支持,而第二改进措施则需要对 Cobertura 源码进行修改,重编译后方可支持。下面将详细介绍如何使用 Cobertura 对上述问题进行优化。

时间: 2024-09-15 04:52:10

利用Java反射(Reflection) 机制提高代码的行覆盖率的相关文章

利用java反射机制调用类的私有方法(推荐)_java

试想一下,如果你可以轻易地调用一个类的私有方法,那么是不是说你的封装都失效了?最近在看java的反射机制,发现居然可以利用java的反射机制去调用其他类的私有方法,至于这能干什么,那就见人见智了.. 我写的一段简易实例代码如下: import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * @author thomaslwq * @version 创建时间:Sep 4, 201

java 利用java反射机制动态加载类的简单实现_java

如下所示: ////////////////// Load.java package org.bromon.reflect; import java.util.ArrayList; import java.util.List; public class Load implements Operator { @Override public List<?> act(List<?> params) { // TODO Auto-generated method stub List<

利用java反射机制实现自动调用类的简单方法_java

1. 新建TestServlet类 package com.yanek.test; import java.io.IOException; import java.lang.reflect.Method; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.ht

利用Oracle执行计划机制提高查询性能

oracle|性能|执行 消耗在准备利用Oracle执行计划机制提高查询性能新的SQL语句的时间是Oracle SQL语句执行时间的最重要的组成部分.但是通过理解Oracle内部产生执行计划的机制,你能够控制Oracle花费在评估连接顺序的时间数量,并且能在大体上提高查询性能. 准备执行SQL语句 当SQL语句进入Oracle的库缓存后,在该语句准备执行之前,将执行下列步骤: 1) 语法检查:检查SQL语句拼写是否正确和词序. 2) 语义分析:核实所有的与数据字典不一致的表和列的名字. 3) 轮

公共技术点之 Java 反射 Reflection

转发自  :http://a.codekk.com/detail/Android/Mr.Simple/%E5%85%AC%E5%85%B1%E6%8A%80%E6%9C%AF%E7%82%B9%E4%B9%8B%20Java%20%E5%8F%8D%E5%B0%84%20Reflection 本文为 Android 开源项目源码解析 公共技术点中的 Java 反射 部分 分析者:Mr.Simple,校对者:Trinea,校对状态:未完成 1. 了解 Java 中的反射 1.1 什么是 Java

利用java反射机制实现自动调用类的方法

 1. 新建TestServlet类 package com.yanek.test; import java.io.IOException; import java.lang.reflect.Method; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.h

一个利用java反射机制的例子

    前一阵写了一篇"用策略模式操作数据库"的文章,一直想用反射机制实现一个测试的类, 今天终于能抽出点空,就写了一个,不过还没有实现把反射的类变量写在xml文件中,这只好下回有空再写了,呵呵   package com; import java.lang.reflect.Constructor;import java.util.Map;import java.util.HashMap; public class Test ...{  public static void main(

Android系统原理与源码分析(1):利用Java反射技术阻止通过按钮关闭对话框

本文为原创,如需转载,请注明作者和出处,谢谢!     众所周知,AlertDialog类用于显示对话框.关于AlertDialog的基本用法在这里就不详细介绍了,网上有很多,读者可以自己搜索.那么本文要介绍的是如何随心所欲地控制AlertDialog.     现在我们来看看第一个需求:如果某个应用需要弹出一个对话框.当单击"确定"按钮时完成某些工作,如果这些工作失败,对话框不能关闭.而当成功完成工作后,则关闭对话框.当然,无论何程度情况,单击"取消"按钮都会关闭

使用Attribute+Reflection来提高代码重用

这篇文章两个目的,一是开阔设计的思路,二是实例代码可以拿来就用. 设计的思路来源于<Effective c#>第一版Item 24: 优先使用声明式编程而不是命令式编程.特别 的地方是,希望提供多个属性的默认排序,而不仅仅只根据一个属性,另外一点是,优先调用对象属性 实现了的IComparable<T>接口,如果没有实现接口,才调用IComparable进行比较.排序类实现 泛型,得到类型安全. 总的思路:Attribute用来装饰我们想要获取元数据的类,使用Reflection来