开始使用 Jacoco 进行代码覆盖
本文介绍了一个示例 Maven 项目,用于入门 Jacoco 的单元测试代码覆盖率。
引言
本文介绍了一个示例 Maven 项目,用于入门 Jacoco 的单元测试代码覆盖率。
背景
通过单元测试来获取代码覆盖率统计数据总是很棒的,而 Jacoco 是最流行的代码覆盖率框架之一。下面是 Eclipse IDE 项目浏览器中显示的示例 Maven 项目。
我在 Windows 计算机上的 JDK-1.7.0_60、Apache Maven 3.2.1 和 Eclipse Java EE IDE for Web Developers Version: Kepler Service Release 2 中测试了此示例项目。
待测试的类
为了演示目的,我创建了以下两个类进行单元测试。
package org.song.example;
public class Student {
private int id;
private String name;
private int score;
public Student(int id, String name, int score) {
this.id = id;
this.name = name;
this.score = score;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getScore() {
return score;
}
}
package org.song.example;
import java.util.HashMap;
public class StudyGroup {
private HashMap<Integer, Student> students;
public StudyGroup() {
students = new HashMap<Integer, Student>();
}
public void addStudent(Student student) {
int id = student.getId();
students.put(id, student);
}
public Student getStudent(int id) {
return students.get(id);
}
public void removeStudent(int id) {
students.remove(id);
}
public void clear() {
students.clear();
}
public int getGroupSize() {
return students.size();
}
}
单元测试类
"StudyGroupTest" 类是用于测试 "Student" 和 "StudyGroup" 类的单元测试类。
package org.song.example;
import java.util.ArrayList;
import java.util.List;
import org.testng.Assert;
import org.testng.annotations.Test;
public class StudyGroupTest {
@Test
public void testStudyGroupClass() {
// prepare the data for test
final List<Student> testStudents = new ArrayList<Student>();
testStudents.add(new Student(1, "Student No.1", 60));
testStudents.add(new Student(2, "Student No.2", 70));
testStudents.add(new Student(3, "Student No.2", 80));
// Start the unit test
final StudyGroup testGroup = new StudyGroup();
for(Student student: testStudents) {
testGroup.addStudent(student);
}
Assert.assertEquals(testGroup.getGroupSize(), testStudents.size());
Student testStudent = testStudents.get(0);
Student returnedStudent = testGroup.getStudent(testStudent.getId());
Assert.assertSame(returnedStudent, testStudent);
Assert.assertEquals(returnedStudent.getId(), testStudent.getId());
Assert.assertEquals(returnedStudent.getName(), testStudent.getName());
Assert.assertEquals(returnedStudent.getScore(), testStudent.getScore());
testGroup.removeStudent(testStudent.getId());
Assert.assertEquals(testGroup.getGroupSize(), testStudents.size() - 1);
testGroup.clear();
Assert.assertEquals(testGroup.getGroupSize(), 0);
}
}
"pom.xml" 文件
"pom.xml" 文件是我们配置 Maven 项目以使用 Jacoco 生成代码覆盖率报告的地方。
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.song.example</groupId>
<artifactId>jacoco-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<testng.version>6.8.8</testng.version>
<java.version>1.7</java.version>
<surefire.version>2.17</surefire.version>
<jacoco.version>0.7.2.201409121644</jacoco.version>
</properties>
<dependencies>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire.version}</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<executions>
<execution>
<id>default-prepare-agent</id>
<goals><goal>prepare-agent</goal></goals>
</execution>
<execution>
<id>default-report</id>
<phase>prepare-package</phase>
<goals><goal>report</goal></goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
- 由于我使用 TestNG 来运行单元测试,因此 TestNG 被添加为 Maven 依赖项;
- 要使用 Jacoco 生成代码覆盖率报告,我们只需在 "pom.xml" 中添加 Jacoco 插件并进行一些非常简单的配置。
如果您的计算机上安装了独立的 Maven ,您只需针对 "pom.xml" 文件发出 "mvn clean install" 命令,即可生成代码覆盖率报告。但在查看代码覆盖率报告之前,让我们先将 Maven 项目导入 Eclipse。
将 Maven 项目导入 Eclipse
理论上,我们不需要任何 IDE 来处理 Maven 项目,但拥有一个 IDE 总是有益的。据我所知,自从 Eclipse Java EE IDE for Web Developers Version: Kepler Service Release 2 起,Eclipse 默认支持 Maven 项目。如果您的 Eclipse 不支持 Maven 项目,我建议升级到更新的版本或自行安装 "m2e "。除了 Maven 支持外,如果在 Eclipse 中还安装了 EclEMMA 插件,那将是很好的。您需要 EclEMMA 插件的原因是 "m2e" 默认不识别 Jacoco 的 Maven 目标 "prepare-agent"。通过安装 EclEMMA 插件,导入附件的 Maven 项目将更加容易。要安装 EclEMMA 插件,您可以从 Eclipse 菜单中选择 "Help" -> "Eclipse Marketplace ..." 并搜索 "EclEMMA"。
安装 EclEMMA 后,您可以从 Eclipse 菜单中选择 "File" -> "Import ..." -> "Maven" -> "Existing Maven Projects" 来导入项目。在浏览到包含 "pom.xml" 文件的文件夹并单击 "Finish" 按钮后,您应该能够成功将 Maven 项目导入到您的 Eclipse 工作空间。
虽然强烈不推荐,但如果您不想安装 EclEMMA 插件,您仍然可以导入附件的 Maven 项目。您需要将以下部分添加到 "lifecycle-mapping-metadata.xml" 文件中,以告知 Eclipse 忽略工作空间中所有 Maven 项目的 Jacoco "prepare-agent" 目标。
<?xml version="1.0" encoding="UTF-8"?>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<versionRange>0.7.2.201409121644</versionRange>
<goals>
<goal>prepare-agent</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore />
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
您可以在 Eclipse 菜单中从 "Window" -> "Preferences" -> "Maven" -> "Lifecycle Mappings" 找到 "lifecycle-mapping-metadata.xml" 文件的位置。
运行 Maven 构建并查看覆盖率报告
您可以通过两种方式构建 Maven 项目。在 Eclipse 中,您可以右键单击 "pom.xml" 文件并运行 Maven 构建。如果您计算机上安装了独立的 Maven,您也可以直接使用您的独立 Maven 对 "pom.xml" 发出 "mvn clean install" 命令。Maven 构建成功后,代码覆盖率报告将在您 Maven 项目的 "target\site\jacoco" 文件夹中准备好。
如果您在浏览器中打开 "index.html" 文件,就可以开始查看代码覆盖率报告了。
然后,您可以按照超链接查看报告的详细信息。
您甚至可以查看文件,了解哪些行被单元测试覆盖或未覆盖。
代码覆盖率 != 软件质量
在软件界,我们一次又一次地听到单元测试和代码覆盖率。强调代码覆盖率的重要性是一个好主意,但我们也应该认识到,代码覆盖率本身并不一定意味着高质量。如果我们看看附件的 Maven 项目,我们有理由感到高兴,因为我们实现了 100% 的代码覆盖率。但让我们进一步看看 "StudyGroup" 类中的以下方法。
public void addStudent(Student student) {
int id = student.getId();
students.put(id, student);
}
我们应该注意到,如果方法中传入的是一个 "null" 学生,即使我们有 100% 的代码覆盖率,我们也会立即遇到运行时异常。我坚信软件的质量是许多因素的综合努力。我认为,清晰定义的功能集、良好的软件架构设计、精心选择的数据结构和算法集,以及一套精心挑选的测试用例,是高质量软件产品的基本构建块。我也认为,那些在编写每一行代码之前负责任地考虑所有可能性的高质量软件工程师是质量的重要组成部分,甚至比我们如何测试产品更重要。无论如何,代码覆盖率报告和 Jacoco 等框架在高质量软件产品中都应该占有一席之地。
关注点
- 本文介绍了一个示例 Maven 项目,用于入门 Jacoco 的单元测试代码覆盖率;
- 它还讨论了将支持 Jacoco 的 Maven 项目导入 Eclipse 时的一些问题以及如何解决这些问题;
- 本文讨论的 EclEMMA 插件是在 Eclipse IDE 中检查代码覆盖率的一个非常好的工具。我没有讨论如何在 Eclipse IDE 中使用它,因为它非常直观且易于使用。它为 Jacoco Maven 插件带来了生产力的巨大提升;
- 代码覆盖率本身并不能保证软件的质量。我们需要在开发阶段的早期就以产品质量为目标,并且需要更加关注如何设计测试用例以在我们工作中实现最佳质量;
- 除了 Jacoco,Cobertura 是另一个流行的 Java 程序代码覆盖率工具。如果您有兴趣,可以看看 Cobertura 。在我最近的经验中,我注意到我无法安装 Cobertura Eclipse 插件,因为它不再支持较新版本的 Eclipse。我希望这个问题能尽快得到解决,以便我们能够继续享受 Cobertura 。
- 我希望您喜欢我的文章,希望本文能以某种方式帮助您。
历史
首次修订 - 2014/10/24。