1 引言
最近在读Bruce Eckel的书《On Java 8》[1](#1),其中第十六章《Validating》有一个用JUnit5进行单元测试的例子。作者用Gradle构建并运行单元测试。因为我对Maven比较熟悉,当即决定用Maven并结合JUnit5实现该例子的运行。
本博文展示:1)如何用Maven的archetype创建一个项目;2)如何写JUnit5的pom.xml依赖和相关配置;3)具体的一个简单的被测代码,并配上一个较详细的单元测试代码;4)如何用mvn test命令进行单元测试。通读本博文,能加深对Maven、JUnit5环境配置和运行机理的理解,为进一步深入学习奠定基础。
所用的环境如下:
| 名称 | 版本 |
|---|---|
| Apache Maven | 3.8.8 |
| Java | 1.8.0_281 |
| 编辑器 | Notepad++ v7.9.1 (64-bit) |
| 运行接口 | Windows系统自带的cmd |
之前我也写过相关的博文《基于JUnit4和JUnit5配合例子讲解JUnit的两种运行方式》,但那篇博文:1)所用的例子太简单,只包含了1个注解@Test;2)没讲解如何用Maven archetype创建项目;3)没讲mvn test。同时,我发现网上的这部分内容碎片较多,不利于重现。本博文克服了上述问题。
2 详细过程
2.1 利用maven创建一个空白项目
在cmd中执行如下命令:
bash
mvn archetype:generate "-DgroupId=com.robert.validating" "-DartifactId=myValidating" "-DarchetypeArtifactId=maven-archetype-quickstart" "-DinteractiveMode=false"
上面命令中,-DgroupId用于指定组织名,实际上是包名称;-DartifactId用于指定项目名或模块名。
运行截图如下:

从上图可见,Maven会自动下载所需要的、本地库中所没有的依赖的jar包。上面命令运行成功后,如下图:

此时,在项目myValidating文件夹下会看到:

同时也生成了与packages对应的文件夹,它们符合Maven的约定,如下举例:

在src/main/java/com/robert/validating/中生成了App.java,在src/test/java/com/robert/validating/中生成了AppTest.java。
2.2 在pom.xml中配置关于JUnit5的依赖和选项
在由Maven生成的pom.xml文件中添加如下内容:
xml
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
</plugins>
</build>
上面每个配置项的具体含义就不在此讲解了,感兴趣的读者,请参考JUnit5的官方介绍。运行本项目时,可能本地库中没有相应的JUnit5的jar包,需确保电脑联网。若运行后,可以在本地库中看到相应的jar包下载成功,如下面截图:

其中,junit-jupiter-api中内容如下:

2.3 被测代码和基于JUnit5的单元测试代码举例
该例子代码完全选自《On Java 8》的第十六章Validating。
在文件夹src/main/java/com/robert/validating/下书写被测代码CountedList.java代码,如下:
java
package com.robert.validating;
import java.util.*;
public class CountedList extends ArrayList<String> {
private static int counter = 0;
private int id = counter++;
public CountedList() {
System.out.println("CountedList #" + id);
}
public int getId() { return id; }
}
在文件夹src/test/java/com/robert/validating/下书写单元测试代码CountedListTest.java代码,如下:
java
package com.robert.validating;
import java.util.*;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
public class CountedListTest {
private CountedList list;
@BeforeAll
static void beforeAllMsg() {
System.out.println(">>> Starting CountedListTest");
}
@AfterAll
static void afterAllMsg() {
System.out.println(">>> Finished CountedListTest");
}
@BeforeEach
public void initialize() {
list = new CountedList();
System.out.println("Set up for " + list.getId());
for(int i = 0; i < 3; i++)
list.add(Integer.toString(i));
}
@AfterEach
public void cleanup() {
System.out.println("Cleaning up " + list.getId());
}
@Test
public void insert() {
System.out.println("Running testInsert()");
assertEquals(list.size(), 3);
list.add(1, "Insert");
assertEquals(list.size(), 4);
assertEquals(list.get(1), "Insert");
}
@Test
public void replace() {
System.out.println("Running testReplace()");
assertEquals(list.size(), 3);
list.set(1, "Replace");
assertEquals(list.size(), 3);
assertEquals(list.get(1), "Replace");
}
// A helper method to simplify the code. As
// long as it's not annotated with @Test, it will
// not be automatically executed by JUnit.
private void compare(List<String> lst, String[] strs) {
assertArrayEquals(lst.toArray(new String[0]), strs);
}
@Test
public void order() {
System.out.println("Running testOrder()");
compare(list, new String[] { "0", "1", "2" });
}
@Test
public void remove() {
System.out.println("Running testRemove()");
assertEquals(list.size(), 3);
list.remove(1);
assertEquals(list.size(), 2);
compare(list, new String[] { "0", "2" });
}
@Test
public void addAll() {
System.out.println("Running testAddAll()");
list.addAll(Arrays.asList(new String[] {
"An", "African", "Swallow"}));
assertEquals(list.size(), 6);
compare(list, new String[] { "0", "1", "2",
"An", "African", "Swallow" });
}
}
从上面单元测试代码中可以看出,其包含了多个注解,对于我们深入理解JUnit5的用法非常有帮助。具体含义,请参考相关书籍。
2.4 运行测试
针对上面例子,运行如下命令:
bash
cd myValidating
mvn test
上面cd命令,切换到Maven生成的项目myValidating文件夹下,以让mvn test看到pom.xml文件。截图如下:



可以看到测试成功。
上面mvn test是运行所有的测试。我们也可以运行针对某个类的测试。看下面例子:
被测代码为:
java
package com.robert.validating;
public class Calculator {
public double add(double number1, double number2) {
return number1 + number2;
}
}
单元测试代码为:
java
package com.robert.validating;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
public class CalculatorTest {
@Test
public void testAdd() {
Calculator calculator = new Calculator();
double result = calculator.add(10, 50);
assertEquals(60, result, 0);
}
}
运行命令:
bash
mvn test -Dtest=CalculatorTest
截图如下:

注意,上面命令中要指定单元测试类的名称。
3 总结
本文详细介绍了如何基于Maven和JUnit5构建并运行单元测试项目,解决了网上相关内容碎片化、重现困难的问题;对mvn test命令也进行了讲解。以激起大家深入学习的兴趣。
- Bruce Eckel. On Java 8. 2017. ↩︎