【SpringBoot深入浅出系列】SpringBoot之集成JUnit5进行单元测试
一、JUnit 5 是什么?
与以前版本的 JUnit 不同,JUnit 5 由来自三个不同子项目的几个不同模块组成。
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
JUnit Platform
是启动 JVM 上测试框架的基础。它还定义了用于开发在平台上运行的测试框架的 API。此外,该平台还提供了一个控制台启动器,用于从命令行启动平台,并提供 JUnit 平台套件引擎,用于使用平台上的一个或多个测试引擎运行自定义测试套件。
JUnit Jupiter
是新的编程模型和扩展模型的组合,用于在 JUnit 5 中编写测试和扩展。Jupiter 子项目提供了一个用于在平台上运行基于 Jupiter 的测试。
JUnit Vintage
提供了一个用于在平台上运行基于 JUnit 3 和 JUnit 4 的测试。
二、为什么使用 JUnit 5 ?
至少有以下优点:
- JUnit 5 利用了Java 8 或更高版本的特性,例如 lambda 函数,使测试更强大,更容易维护。
- JUnit 5 可以同时使用多个扩展,可以轻松地将 Spring 扩展与其他扩展(如自定义扩展)结合起来,包容性强,可以接入其他的测试引擎。
- 功能更强大,提供了新的断言机制、参数化测试、重复性测试等新功能。
从 JUnit 4 切换到 JUnit 5 非常简单,步骤如下:
- 将库和构建系统从 JUnit 4 更新到 JUnit 5。确保在测试运行时路径中包含 junit-vintage-engine ,以允许现有的测试执行。
- 使用新的 JUnit 5 构造开始构建新的测试。
三、JUnit 4 和 JUnit 5 的区别
1.导入
JUnit 5 使用新的 org.junit.jupiter 包,Junit 4 使用的是 org.junit 包。
2.@Test 注解
@Test 注解不再有参数,每个参数都被移到了一个函数中。
3.超时写法发生变化
4.其它注解
@Before 变成了 @BeforeEach
@After 变成了 @AfterEach
@BeforeClass 变成了 @BeforeAll
@AfterClass 变成了 @AfterAll
@Ignore 变成了 @Disabled
@Category 变成了 @Tag
@Rule 和 @ClassRule 没有了,用 @ExtendWith 和 @RegisterExtension 代替
5.断言
JUnit 5 断言在 org.junit.jupiter.api.Assertions 中。大多数常见的断言,如 assertEquals() 和 assertNotNull(),和以前有所不同,错误信息放在了最后一个参数,例如:
assertEquals(“my message”,1,2) 变为 assertEquals(1,2,”my message”)
大多数断言现在接受一个构造错误信息的lambda,只有当断言失败时才会被调用。
6.假设
假设已被移至 org.junit.jupiter.api.Assumptions。
同样的假设也存在,但它们支持 BooleanSupplier 以及 Hamcrest 匹配器(http://hamcrest.org/)来匹配条件。Lambdas(类型为 Executable)可以用来在条件满足时执行代码。
7.扩展
在 JUnit 4中,自定义框架通常意味着使用 @RunWith 注释来指定一个自定义的运行器。使用多个运行器是有问题的,通常需要链式或使用 @Rule。在 JUnit 5 中已经得到了简化和改进。
四、创建项目集成 JUnit 5 进行单元测试
1.项目说明
新建 Spring Initializr 项目 junit,项目下新建 entity、controller 类。项目创建示例说明 JUnit 5 的使用。
项目目录结构:
2.创建 Spring Initializr 项目 junit
(1)添加依赖
添加依赖,如果已按截图操作,pom.xml 还需引入 Junit 5 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
完整的 pom.xml 文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.chaoyue</groupId>
<artifactId>junit</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>junit</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
(2)添加配置
application.yml 文件中添加如下配置:
server:
port: 8080
(3)新建实体类 User
为减少不必要的代码,引入 lombok 依赖。实体类代码如下:
package com.chaoyue.junit.entity;
import lombok.Data;
@Data
public class User {
// @TableId(type = IdType.AUTO)
private Long id; // id
private String username; // 用户名
private String password; // 密码
}
(4)新建控制类 LoginController
package com.chaoyue.junit.controller;
import com.chaoyue.junit.entity.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class LoginController {
@GetMapping("getUserInfo")
public String getUserInfo() {
// 获取用户信息
User userInfo = new User();
userInfo.setId(1L);
userInfo.setUsername("admin");
userInfo.setPassword("123456");
return userInfo.toString();
}
}
3.创建 Junit 5 测试示例
(1)创建测试类 LoginControllerTest
在LoginController 类上使用快捷键 ctrl + shift + t 打开弹窗:
点击【Create New Test…】创建测试类:
(2)添加注解
测试类添加注解 @SpringBootTest:
package com.chaoyue.junit.controller;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class LoginControllerTest {
@BeforeEach
void setUp() {
}
@AfterEach
void tearDown() {
}
@Test
void getUserInfo() {
}
}
(3)测试示例
①断言:判断条件是否为 true 或 false
/**
* assertTrue、assertFalse 判断条件是否为 true 或 false
*/
@Test
@DisplayName("测试断言 assertTrue、assertTrue")
void testTrue() {
assertTrue(1 > 2 );
// assertFalse(1 > 2 );
}
②断言:判断两个值是否相等
/**
* assertEquals、assertNotEquals 判断两个值是否相等
*/
@Test
@DisplayName("测试断言 assertEquals、assertNotEquals")
void testEquals() {
assertEquals(1, 2 );
// assertNotEquals(1, 2 );
}
③断言:判断条件是否为 null 或 not null
/**
* assertNull、assertNotNull 判断条件是否为 null 或 not null
*/
@Test
@DisplayName("测试断言 assertNull、assertNotNull")
void testNotNull() {
assertNotNull(new Object());
// assertNull(new Object());
}
④断言:判断断言是否超时
/**
* assertTimeout 判断断言是否超时
*/
@Test
@DisplayName("测试断言 assertTimeout")
void testTimeout() {
String result = assertTimeout(ofSeconds(2),() -> {
Thread.sleep(1000);
return "未超时";
});
System.out.println(result);
}
⑤断言:组合断言
/**
* assertAll 组合断言,当内部所有断言都正确执行才算通过
*/
@Test
@DisplayName("测试组合断言")
void testTAll() {
assertAll("测试组合断言",
() -> {
assertTrue(1 < 2, "断言1");
},
() -> {
assertNotNull(new Object(), "断言2");
},
() -> {
assertEquals(1,1);
}
);
}
⑥重复测试
/**
* RepeatedTest 重复测试
*/
@RepeatedTest(3)
@DisplayName("重复测试")
void testRepeat() {
System.out.println("重复测试");
}
⑦参数化测试
/**
* ParameterizedTest 参数化测试
*/
@ParameterizedTest
@ValueSource(strings = {"参数1", "参数2", "参数3"})
@DisplayName("参数化测试")
void testParam(String param) {
assertEquals(param, "参数1");
System.out.println(param);
}
PS:JUnit 5 官网:https://junit.org/junit5/
- 点赞
- 收藏
- 关注作者
评论(0)