Cobertura 统计多模块maven项目测试覆盖率

本文介绍如何使用Cobertura工具统计单元测试覆盖率,包括单模块和多模块Maven项目的设置方法,通过实例展示了覆盖率报告的生成过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


Cobertura 统计单元测试覆盖率的机制:运行类,并在一个log文件中记录哪一行被执行,然后将源代码和log文件进行比对。


1. 简单的情况:单模块maven项目

项目结构


源代码 src/main/java/se/sigma/calculator/Calculator.java

package se.sigma.calculator;

public class Calculator {
    public int nextFibonacci(int a, int b) {
        return a + b;
    }
}
单元测试 src/test/java/se/sigma/calculator/CalculatorTest.java

package se.sigma.calculator;

import org.junit.Test;

import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;

public class CalculatorTest {

    @Test
    public void shouldCalculateFibonacci() {
        Calculator calculator = new Calculator();
        int expected = 13;
        int actual = calculator.nextFibonacci(5, 8);

        assertThat(actual, is(expected));
    }
}

我们添加maven cobetura插件

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>se.thinkcode</groupId>
    <artifactId>one-module-example</artifactId>
    <version>1.0</version>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>cobertura-maven-plugin</artifactId>
                <version>2.5.1</version>
                <executions>
                    <execution>
                        <phase>process-classes</phase>
                        <goals>
                            <goal>cobertura</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>
    </dependencies>
</project>
关键部分是 cobertura-maven-plugin,它在项目的process-classes阶段被执行,生成的报告如下所示



上图显示覆盖率是100%,两行代码都被执行到。代码没有分支,复杂度是1。

点击进入package se.sigma.calculator里面会得到如下结果图:


点击进入 Calculator 这个类会得到:


cobetura不仅会记录每行是否被执行到,还会记录每行被执行的次数(hits)。


2. 复杂的情况:多模块maven项目


在多模块项目中,我们往往会把源代码放在一个模块,测试代码放在另一个模块(出于项目分解管理或配置文件的原因)。而maven的模块是一个执行完再执行另一个,导致cobetura的代码覆盖率统计出现问题。

现有多模块项目结构如下:



      功能源代码放在product模块,单元测试代码放在test模块。java文件的内容和前面的一样。test模块添加了product模块的依赖,这样就可以import Calculator这个类。

根项目的pom.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>se.thinkcode</groupId>
    <artifactId>multi-module-failing-example</artifactId>
    <version>1.0</version>
    <packaging>pom</packaging>
    <modules>
        <module>product</module>
        <module>test</module>
    </modules>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>cobertura-maven-plugin</artifactId>
                <version>2.5.1</version>
                <executions>
                    <execution>
                        <phase>post-integration-test</phase>
                        <goals>
                            <goal>cobertura</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>
    </dependencies>
</project>

<modules>标签里包含它的两个模块 product 和test。同样加上了 cobertura-maven-plugin来生成测试覆盖率报告. 

product模块的pom.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>se.thinkcode</groupId>
        <artifactId>multi-module-failing-example</artifactId>
        <version>1.0</version>
    </parent>
    <groupId>se.thinkcode</groupId>
    <artifactId>calculator</artifactId>
    <version>1.0</version>
</project>

test模块的pom.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>se.thinkcode</groupId>
        <artifactId>multi-module-failing-example</artifactId>
        <version>1.0</version>
    </parent>
    <groupId>se.thinkcode</groupId>
    <artifactId>calculator-test</artifactId>
    <version>1.0</version>
    <dependencies>
        <dependency>
            <groupId>se.thinkcode</groupId>
            <artifactId>calculator</artifactId>
            <version>1.0</version>
        </dependency>
    </dependencies>
</project>

依赖 product 的源代码,才能引用 Calculator并调用它的方法。

测试结果如下:


可以看到覆盖率是0,而我们知道正确的结果应该是100%。

点击进入package里面,显示Calculator.java没有被执行


点击进入Calculator.java里面,显示方法代码没有被执行


maven的项目结构是固定的,一个java模块生成一个target文件,各模块是隔离的,因此无法跨模块比对运行的测试类和源代码文件。这时我们想到另一种项目管理工具Ant。Ant与maven相比,它的灵活在于可以让用户自己指定源码的位置和编译后字节码的存放位置,如此又可以达到单模块maven项目的效果。

maven+Ant

在前面工程结构的基础上,我们在根目录加上Ant的 build.xml 文件。

multi-module-example
|-- build.xml
|-- pom.xml
|-- product
|   |-- pom.xml
|   `-- src
|       `-- main
|           `-- java
|               `-- se
|                   `-- sigma
|                       `-- calculator
|                           `-- Calculator.java
`-- test
    |-- pom.xml
    `-- src
        `-- test
            `-- java
                `-- se
                    `-- sigma
                        `-- calculator
                            `-- CalculatorTest.java

父项目根目录pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>se.thinkcode</groupId>
    <artifactId>multi-module-example</artifactId>
    <version>1.1</version>
    <packaging>pom</packaging>
    <modules>
        <module>product</module>
        <module>test</module>
    </modules>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-clean-plugin</artifactId>
                <version>2.4.1</version>
                <configuration>
                    <filesets>
                        <fileset>
                            <directory>.</directory>
                            <includes>
                                <include>**/*.ser</include>
                            </includes>
                        </fileset>
                    </filesets>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>
    </dependencies>
</project>

maven-clean-plugin是用来清楚之前生成的target文件(.ser结尾的是cobetura生成的报告)。这里不用再添加maven-cobetura插件,cobertura需要单独下载,在Ant 脚本中引用。

Cobertura.下载地址 http://cobertura.sourceforge.net/download.html 。我这里用的是Cobertura 1.9.4.1下载后解压到 /Users/tsu/java/cobertura-1.9.4.1 ,你可以修改成其他目录,但是要注意和 build.xml 中指定的路径一致。

Ant构建脚本build.xml:

<project>
    <target name="instrument">
        <!-- Add all modules that should be included below -->
        <!-- <antcall target="instrumentAModule">
            <param name="module" value="MODULE_NAME_TO_REPLACE"/>
        </antcall> -->
        <antcall target="instrumentAModule">
            <param name="module" value="product"/>
        </antcall>
    </target>

    <target name="report" depends="merge">
        <property name="src.dir" value="src/main/java/"/>
        <cobertura-report datafile="sum.ser"
                          format="html"
                          destdir="./target/report">
            <!-- Add all modules that should be included below -->
            <!-- fileset dir="./MODULE_NAME_TO_REPLACE/${src.dir}"/ -->
            <fileset dir="./product/${src.dir}"/>
        </cobertura-report>
    </target>

    <target name="merge">
        <cobertura-merge datafile="sum.ser">
            <fileset dir=".">
                <include name="**/cobertura.ser"/>
            </fileset>
        </cobertura-merge>
    </target>

    <target name="instrumentAModule">
        <property name="classes.dir" value="target/classes"/>
        <cobertura-instrument todir="./${module}/${classes.dir}">
            <fileset dir="./${module}/target/classes">
                <include name="**/*.class"/>
            </fileset>
        </cobertura-instrument>
    </target>

    <property environment="env"/>
    <property name="COBERTURA_HOME" value="/Users/tsu/java/cobertura-1.9.4.1"/>
    <property name="cobertura.dir" value="${COBERTURA_HOME}"/>
    <path id="cobertura.classpath">
        <fileset dir="${cobertura.dir}">
            <include name="cobertura.jar"/>
            <include name="lib/**/*.jar"/>
        </fileset>
    </path>
    <taskdef classpathref="cobertura.classpath" resource="tasks.properties"/>
</project>

两个重要的<targets>是:

  • instrument
  • report

instrument 用Cobertura instrumentation来执行编译类文件。

report 将执行的测试类和源代码组合起来,生成覆盖率报告。

product模块的pom.xml和之前一致 :

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>se.thinkcode</groupId>
        <artifactId>multi-module-example</artifactId>
        <version>1.1</version>
    </parent>
    <groupId>se.thinkcode</groupId>
    <artifactId>calculator</artifactId>
    <version>1.1</version>
</project>

test模块的pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>se.thinkcode</groupId>
        <artifactId>multi-module-example</artifactId>
        <version>1.1</version>
    </parent>
    <groupId>se.thinkcode</groupId>
    <artifactId>calculator-test</artifactId>
    <version>1.1</version>
    <dependencies>
        <dependency>
            <groupId>se.thinkcode</groupId>
            <artifactId>calculator</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>net.sourceforge.cobertura</groupId>
            <artifactId>cobertura</artifactId>
            <version>1.9.4.1</version>
        </dependency>
    </dependencies>
</project>

test 模块需要添加 Cobertura依赖,这样在test phase才能记录执行的踪迹

项目的执行有以下四步:

  1. Compile all code
  2. Instrument the code
  3. Execute all tests
  4. Consolidate and build the report

即顺序执行下面的四条命令:

	mvn clean compile
	ant instrument
	mvn test
	ant report

生成的报告:





### 推荐的测试用例覆盖率检测工具 以下是几种广泛使用的专业工具或软件,它们可以有效帮助开发人员和测试团队评估并提升项目测试用例覆盖率: #### 1. **JaCoCo** JaCoCo 是一种开源 Java 字节码分析工具,专注于代码覆盖率测量。它可以生成详细的报告,展示哪些部分的代码已被测试覆盖,哪些尚未被覆盖。其支持多种集成方式,包括 Maven 和 Gradle 插件[^1]。 ```bash # 使用 JaCoCo 的 Maven 配置示例 <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.7</version> <executions> <execution> <goals> <goal>prepare-agent</goal> </goals> </execution> </executions> </plugin> ``` #### 2. **Cobertura** Cobertura 是另一种流行的开源工具,主要用于 Java 应用程序的代码覆盖率分析。它提供了图形化的 HTML 报告,便于开发者快速识别未覆盖的部分。此外,Cobertura 支持与 Jenkins 等 CI 工具无缝集成[^4]。 ```xml <!-- Cobertura Maven Plugin 示例 --> <plugin> <groupId>net.sourceforge.cobertura</groupId> <artifactId>cobertura-maven-plugin</artifactId> <version>2.7</version> </plugin> ``` #### 3. **SonarQube** 虽然 SonarQube 主要是一个静态代码分析平台,但它也可以通过插件扩展实现对测试覆盖率的支持。该工具不仅提供代码质量和安全性的全面视图,还允许用户跟踪单元测试、集成测试等功能测试覆盖率变化趋势[^3]。 #### 4. **Istanbul (nyc)** 针对 JavaScript 或 TypeScript 开发者,Istanbul 提供了一种简单而强大的解决方案来计算函数级和语句级别的覆盖率。它的 CLI 工具 `nyc` 可轻松嵌入到 Node.js 测试流程中[^2]。 ```javascript // Istanbul nyc 命令行配置示例 nyc mocha test/**/*.spec.js --reporter lcov ``` #### 5. **pytest-cov** 如果项目基于 Python 构建,则 pytest-cov 是一个非常实用的选择。作为 pytest 的扩展模块之一,它能高效地收集 PyTest 运行期间产生的覆盖率数据,并以多种形式呈现给最终用户。 ```python # 安装 pytest-cov 并运行测试命令 pip install pytest-cov pytest --cov=your_module tests/ ``` --- ### 总结 上述提到的各种工具有各自的特点和技术栈适配能力,在实际应用时可以根据具体编程语言环境和个人偏好做出选择。无论是哪种技术方案,都应注重持续改进测试策略,从而不断提高整体产品质量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值