用Maven的quickstart archetype创建项目并结合JUnit5单元测试
1 引言
最近在读Bruce Eckel的书《On Java 8》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中执行如下命令:
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文件中添加如下内容:
<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代码,如下:
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代码,如下:
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;@BeforeAllstatic void beforeAllMsg() {System.out.println(">>> Starting CountedListTest");}@AfterAllstatic void afterAllMsg() {System.out.println(">>> Finished CountedListTest");}@BeforeEachpublic 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));}@AfterEachpublic void cleanup() {System.out.println("Cleaning up " + list.getId());}@Testpublic void insert() {System.out.println("Running testInsert()");assertEquals(list.size(), 3);list.add(1, "Insert");assertEquals(list.size(), 4);assertEquals(list.get(1), "Insert");}@Testpublic 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);}@Testpublic void order() {System.out.println("Running testOrder()");compare(list, new String[] { "0", "1", "2" });}@Testpublic void remove() {System.out.println("Running testRemove()");assertEquals(list.size(), 3);list.remove(1);assertEquals(list.size(), 2);compare(list, new String[] { "0", "2" });}@Testpublic 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 运行测试
针对上面例子,运行如下命令:
cd myValidating
mvn test
上面cd命令,切换到Maven生成的项目myValidating文件夹下,以让mvn test看到pom.xml文件。截图如下:



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

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