如何使用Cobertura和反射机制来提高Java单元测试中的代码覆盖率2014-10-17 ibm 王传阳, 刘伏亮
引言
单元测试是软件开发过程中重要的质量保证环节。单元测试可以减少代码中潜在的错误,使缺陷更早地被发现,从而降低了软件的维护成本。软件代码的质量由单元测试来保证,而单元测试自身的质量与效率问题也不容忽视。提高单元测试的质量与效率,不仅能够使软件代码更加有保证,而且能够节省开发人员编写或者修改单元测试代码的时间。衡量单元测试质量与效率的指标多种多样,代码覆盖率是其中一个极为重要的指标。一般而言,代码覆盖率越高,单元测试覆盖的范围就越大,代码中潜在错误的数量就越少,软件质量就越高。本文首先介绍代码覆盖率的统计指标类型及常用统计工具,然后重点选取具有代表性的行覆盖率进行分析,介绍两种方法用于提高代码的行覆盖率。
代码覆盖率的统计指标
代码覆盖率指的是一种衡量代码覆盖程度的方式,通常会对以下几种方式进行统计分析:行覆盖。它又被称作语句覆盖或基本块覆盖。这是一种较为常用且具有代表性的指标,度量的是被测代码中每个可执行语句是否被执行到。条件覆盖。它度量的是当代码中存在分支时,是否能覆盖进入分支和不进入分支这两种情况。这要求开发人员编写多个测试用例以分别满足进入分支与不进入分支这两种情况。路径覆盖。它度量的是当代码中存在多个分支时,是否覆盖到分支之间不同组合方式所产生的全部路径。这是一种力度最强的覆盖检测,相对而言,条件覆盖只是路径覆盖中的一部分。在这三种覆盖指标中,行覆盖简单,适用性广,但可能会被认为是“最弱的覆盖”,其实不然。行覆盖相对于条件或路径覆盖,可以使开发人员通过尽可能少的测试数据和用例,覆盖尽可能多的代码。通常情况下,是先通过工具检测一遍整个工程单元测试的行覆盖情况,然后针对没有被覆盖到的代码,分析其没有被覆盖到的原因。如果是由于该代码所在分支由于不满足进入该分支的条件而没有被覆盖,那么开发人员才会进一步修改或增加测试代码,完成该部分的条件或路径覆盖。可见,高效高质量的行覆盖是有效进行条件覆盖与路径覆盖的前提。行覆盖率越高,说明没有被覆盖到的代码越少,这样开发人员便会集中精力修改测试用例,覆盖这些数量不多的代码。相反,如果行覆盖率低,开发人员需要逐个检查没有被覆盖到的代码,精力被分散,因此很难提高剩余代码单元测试的质量。代码覆盖率 = 被测代码行数 / 参测代码总行数 * 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:

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>
URL:http://www.bianceng.cn/Programming/project/201410/45935.htm