一、Junit5的架构
JUnit 5由三个不同子项目中的几个不同模块组成。JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage,如下:
- JUnit platform:为了能够启动 junit 测试,IDE、构建工具或者插件需要包含和扩展平台 API。它定义了用于开发在平台上运行的新测试框架的“TestEngine”API。它还提供了一个控制台启动器来从命令行启动平台并为 Gradle 和 Maven 构建插件。
- JUnit Jupiter:它包括用于编写测试的新编程和扩展模型。它具有所有新的 junit 注释和
TestEngine
实现来运行使用这些注释编写的测试。 - JUnit Vintage:它的主要目的是支持在 JUnit 5 平台上运行 JUnit 3 和 JUnit 4 书面测试。它有向后兼容性。
参考网站如下:
官网地址:https://junit.org/junit5/
官方入门文档:https://junit.org/junit5/docs/current/user-guide/#overview
官方例子:https://github.com/junit-team/junit5-samples
官方github:https://github.com/junit-team
二、安装
可以在 maven 或者 gradle 项目中使用 JUnit 5,下面展示在Maven中使用:
<dependencies>
<!--配置和加载测试计划的公共API – 典型的使用场景是IDE和构建工具-->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.7.0</version>
<scope>test</scope>
</dependency>
<!--JUnit Jupiter测试引擎的实现,仅仅在运行时需要。-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<!--JUnit Vintage测试引擎实现,允许在新的JUnit Platform上运行低版本的JUnit测试,即那些以JUnit 3或JUnit 4风格编写的测试。
非必须
-->
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<!--编写测试 和 扩展 的JUnit Jupiter API。-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</version>
</dependency>
</dependencies>
三、JUnit 5 注释
JUnit 5 提供以下注释来编写测试:
Annotation | 描述 |
| 带注释的方法将在测试类中的每个测试方法之前运行。 |
| 带注释的方法将在测试类中的每个测试方法之后运行。 |
| 带注释的方法将在测试类中的所有测试方法之前运行。这个方法必须是静态的。 |
| 带注释的方法将在测试类中的所有测试方法之后运行。这个方法必须是静态的。 |
| 它用于将方法标记为junit测试 |
| 用于为测试类或者测试方法提供任何自定义显示名称 |
| 它用于禁用或者忽略测试套件中的测试类或者方法。 |
| 用于创建嵌套测试类 |
| 用标签标记测试方法或者测试类以进行测试发现和过滤 |
| 标记方法是动态测试的测试工厂 |
| 表示方法是参数化测试 |
@RepeatedTest | 表示方法是重复测试模板 |
@ExtendWith | 用于注册自定义扩展,该注解可以继承 |
@FixMethodOrder(MethodSorters.NAME_ASCENDING) | 控制测试类中方法执行的顺序,这种测试方式将按方法名称的进行排序,由于是按字符的字典顺序,所以以这种方式指定执行顺序会始终保持一致;不过这种方式需要对测试方法有一定的命名规则,如 测试方法均以testNNN开头(NNN表示测试方法序列号 001-999) |
四、案例演示
4.1.测试:Hello World
创建Maven项目,导入Junit5依赖后,创建包 com.easybuy.method,在下面创建 类 Calculator(模拟我们需要测试的功能):
public class Calculator {
public int add(int num1, int num2){
return num1+num2;
}
public int sub(int num1, int num2){
return num1-num2;
}
}
创建com.easybuy.test包,在下面编写测试用例,创建 HelloWorld,代码如下:
public class HelloWorld {
@Test
public void testSub(){
Calculator calculator = new Calculator();
//调用减法
int num = calculator.sub(10, 12);
//分别传入预期结果和实际结果
Assertions.assertEquals(-12,num);
}
@Test
public void testAdd(){
Calculator calculator = new Calculator();
//调用减法
int num = calculator.add(10, 12);
//分别传入预期结果和实际结果
Assertions.assertEquals(22,num);
}
}
点击 类名或者方法名左边的按钮即可执行用例如下:
@Test注解在方法上标记方法为测试方法,以便构建工具和 IDE 能够识别并执行它们。
注意的用例在idea中执行的时候一直报错缺少:org.junit.jupiter.api,这个问题很奇怪,需要再idea中勾选如下配置,其他的项目不用勾选
4.2.给用例添加上前置和后置
添加上前置和后置后,完成的案例代码如下:
public class AppTest {
@BeforeAll
static void setup(){
System.out.println("@BeforeAll 将在测试类中的所有测试方法之前运行。这个方法必须是静态的");
}
@BeforeEach
void setupThis(){
System.out.println("@BeforeEach 将在测试类中的每个测试方法之前运行");
}
@Test
public void testSub(){
Calculator calculator = new Calculator();
//调用减法
int num = calculator.sub(10, 12);
//分别传入预期结果和实际结果
Assertions.assertEquals(-2,num);
System.out.println("我是testSub");
}
@Test
public void testAdd(){
Calculator calculator = new Calculator();
//调用减法
int num = calculator.add(10, 12);
//分别传入预期结果和实际结果
Assertions.assertEquals(22,num);
System.out.println("我是testAdd");
}
@AfterEach
void tearThis(){
System.out.println("@AfterEach 将在测试类中的每个测试方法之后运行。");
}
@AfterAll
static void tear(){
System.out.println("@AfterAll 将在测试类中的所有测试方法之后运行。这个方法必须是静态的");
}
}
执行后结果如:
4.3.禁用执行某条用例
@Disabled注解可以添加给某个用例方法,或者类上,添加后面该类或者该方法不会在执行
五、断言测试
断言有助于使用测试用例的实际输出来验证预期输出。为了简单起见,所有 JUnit Jupiter 断言都是 org.junit.jupiter.Assertions 类中的静态方法,例如
-
assertEquals
断言预期值和实际值相等 - assertNotEquals 断言预期值和实际值不相等,则测试通过
-
assertAll
分组断言,执行其中包含的所有断言 -
assertArrayEquals
断言预期数组和实际数组相等 -
assertFalse
断言条件为假 -
assertNotNull
断言不为空 -
assertSame
断言两个对象相等 -
assertTimeout
断言超时 -
fail
使单元测试失败
案例代码如下:
public class AssertionsTest {
@Test
public void test1(){
//Test will pass
Assertions.assertEquals(3,3);
}
@Test
public void test2(){
//Test will fail
Assertions.assertEquals(4, 4);
}
@Test
public void test3(){
int[] ints1 = new int[1024];
int[] ints2 = new int[1024];
//Test will fail 断言预期数组和实际数组相等
Assertions.assertArrayEquals(ints1,ints2);
}
@Test
public void test4(){
ArrayList<String> strings = new ArrayList<>();
strings.add("李明月");
strings.add("刘明月");
strings.add("张如月");
//Test will fail 断言预期集合中是否包含某个值
Assertions.assertTrue(strings.contains("张如月"));
}
}
六、参数化测试
6.1.引入参数化所需依赖
实现参数化需要将下面的依赖引入到pom.xml,注意版本要和之前引入的Junit版本保持一致
dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.7.0</version>
<scope>compile</scope>
</dependency>
6.2.使用注解ParameterizedTest
使用@ParameterizedTest注解来代替@Test注解,单参数化注解使用@ValueSource ,多参数注解 @CsvSource,如果这两个注解@ParameterizedTest@Test同时使用则会多执行一次
- 单个参数如下:
public class ParamsTest {
//@ParameterizedTest注解来代替@Test注解,单参数化注解使用@ValueSource
@ParameterizedTest
@ValueSource(strings = {"张三", "小明", "小红"})//几个参数,就执行几次
void testParamString(String name){
System.out.println(name);
}
@ParameterizedTest
@ValueSource(ints = {10,20,30})
void testParamInt(int num){
Assertions.assertTrue(num < 100);
System.out.println(num);
}
}
- 多参数时,默认参数之间用英文逗号隔开
public class ParamsTest {
//@ParameterizedTest注解来代替@Test注解,单参数化注解使用@ValueSource
@ParameterizedTest
@ValueSource(strings = {"张三", "小明", "小红"})//几个参数,就执行几次
void testParamString(String name){
System.out.println(name);
}
@ParameterizedTest
@ValueSource(ints = {10,20,30})
void testParamInt(int num){
Assertions.assertTrue(num < 100);
System.out.println(num);
}
//多参数
@ParameterizedTest
@CsvSource({"小明,10,一班","小李,11,二班"})//多参数使用@CsvSource注解实现,这里传入了两组数据,分别传入中间使用逗号隔开
public void MultParams(String name,int age,String className ){
System.out.println("name:"+name+" age:"+age+" classname:"+className);
}
}
- 参数文件:参数可以从指定csv文件中读取,在resources目录下创建csv文件,参数之间用相同分隔符分割,例如 “,”,"|"等。此时使用的注解为 @CsvFileSource
下面例如创建一个csv文件如data.csv,需要注意如果csv文件中有中文,需要通过记事本打开csv文件修改编码方式为utf-8,否则读取进来后中文会乱码,数据创建多列直接使用逗号隔开,存放在resource目录下:
在代码中读取csv文件中的数据作为参数化的信息如下:
public class ParamsTest {
//@ParameterizedTest注解来代替@Test注解,单参数化注解使用@ValueSource
@ParameterizedTest
@ValueSource(strings = {"张三", "小明", "小红"})//几个参数,就执行几次
void testParamString(String name){
System.out.println(name);
}
@ParameterizedTest
@ValueSource(ints = {10,20,30})
void testParamInt(int num){
Assertions.assertTrue(num < 100);
System.out.println(num);
}
//多参数
@ParameterizedTest
@CsvSource({"小明,10,一班","小李,11,二班"})//多参数使用@CsvSource注解实现,这里传入了两组数据,分别传入中间使用逗号隔开
public void MultParams(String name,int age,String className ){
System.out.println("name:"+name+" age:"+age+" classname:"+className);
}
@ParameterizedTest
@CsvFileSource(resources = "/data.csv")//读取csv文件中的信息
public void ParamByFile(String name, String password) {
System.out.println("name:" + name + " password:" + password);
}
}
执行后效果如下:
七、测试套件
使用 JUnit 5 测试套件,我们可以运行分布到多个测试类和不同包中的测试。JUnit 5 提供了两个注解:@SelectPackages 和@SelectClasses 来创建测试套件。要执行该套件,我们将使用 @RunWith(JUnitPlatform.class)
。需要引入依赖如下:
<!--创建测试套件-->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite-api</artifactId>
<version>1.6.3</version>
</dependency>
<!--执行测试套件-->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-runner</artifactId>
<version>1.6.3</version>
</dependency>
- 执行某个包下面的所有测试用例如下:
@RunWith(JUnitPlatform.class)
@SelectPackages("com.easybuy.test")//加载执行包com.easybuy.test下的所有测试用例
public class JUnit5TestSuiteExample {
}
- 指定执行某个类下的用例
@RunWith(JUnitPlatform.class)
@SelectClasses({
com.easybuy.test.ParamsTest.class,
com.easybuy.test.AppTest.class
})//在测试套件中加载执行ParamsTest和AppTest类
public class JUnit5TestClassSuiteExample {
}
此外,我们可以使用以下注释来过滤测试包、类甚至测试方法。
-
@IncludePackages
和@ExcludePackages
过滤包 -
@IncludeClassNamePatterns
和@ExcludeClassNamePatterns
来过滤测试类 -
@IncludeTags
和@ExcludeTags
来过滤测试方法
@RunWith(JUnitPlatform.class)
@SelectPackages("com.easybuy.test")//加载执行包com.easybuy.test下的所有测试用例
@ExcludeClassNamePatterns("com.easybuy.test.ParamsTest")//排除ParamsTest类
public class JUnit5TestSuiteExample {
}
执行结果如下图:
八、Junit5结合allure生成测试报告
8.1.下载和安装allrue
由于Junit的测试结果展示过于简陋,可以借助于allure生成可视化的测试报告更加的美观同时易用性也不错,allure是一个通用的测试报告框架,
GitHub下载地址:https://github.com/allure-framework/allure2/releases,进入后选择版本下载即可:
需要java8+,JDK 1.8+环境,所以提前配置好java环境,将安装包解压后配置allure环境变量,在系统变量path中添加到bin,例如:
D:\allure-2.21.0\bin
配置成功后在命令窗口输入allure或者allure --version,可以看到我的allure版本是2.21.0
8.2.allure集成Junit5依赖导入
在pom.xml中添加依赖,然后进行配置:
<properties>
<aspectj.version>1.9.5</aspectj.version>
<allure.version>2.13.2</allure.version>
</properties>
<dependencies>
<!--配置和加载测试计划的公共API – 典型的使用场景是IDE和构建工具-->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.7.0</version>
<scope>test</scope>
</dependency>
<!--JUnit Jupiter测试引擎的实现,仅仅在运行时需要。-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<!--JUnit Vintage测试引擎实现,允许在新的JUnit Platform上运行低版本的JUnit测试,即那些以JUnit 3或JUnit 4风格编写的测试。
非必须
-->
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<!--编写测试 和 扩展 的JUnit Jupiter API。-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</version>
</dependency>
<!--参数化-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.7.0</version>
<scope>compile</scope>
</dependency>
<!--创建测试套件-->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite-api</artifactId>
<version>1.6.3</version>
</dependency>
<!--执行测试套件-->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-runner</artifactId>
<version>1.6.3</version>
</dependency>
<!--SL4J 主要是为了给Java日志访问提供一个标准、规范的API框架,其主要意义在于提供接口,具体的实现可以交由其他日志框架
我的用例执行的时候console中一直提示缺少,故而添加
-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.2</version>
</dependency>
<!--Junit5集成allure-->
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-junit5</artifactId>
<version>2.13.2</version>
</dependency>
</dependencies>
<!--junit allure maven运行测试用例jar包-->
<build>
<plugins>
<!--junit allure maven运行测试用例jar包-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<argLine>
-javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
</argLine>
</configuration>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-maven</artifactId>
<version>2.10.0</version>
<configuration>
<reportVersion>${allure.version}</reportVersion>
<allureDownloadUrl>/maven2/io/qameta/allure/allure-commandline/${allure.version}/allure-commandline-${allure.version}.zip</allureDownloadUrl>
</configuration>
</plugin>
</plugins>
</build>
上面会设置后。执行用例(可以根据测试套件执行,也可以执行某个模块还是跟之前一样)生成的结果以josn格式存放在allure-results在根目录下:
8.3.生成allure测试报告
8.3.1.生成在线的测试报告
生成在线版本的测试报告,在命令行打开执行即可
allure serve allure-results
如下图:
8.3.1.生成文件持久型测试报告
先使用 allure generate 生成htl格式的测试结果报告
allure generate ./allure-results -o ./fileresult --clean
注意:上面的命令将 ./allure-results 目录下的测试数据生成html测试报告到 ./fileresult 路径下。–clean 先清空测试报告目录,再生成新的测试报告。
打开测试报告:
allure open -h 127.0.0.1 -p 8885 ./fileresult
启动一个web服务,将已经生成的html测试报告在默认的浏览器中打开,地址为:http://localhost:8885/
在浏览器打开测试报告如下: