SpringBoot2中JUnit5单元测试总结
1、JUnit5整体概述
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
JUnit Platform: Junit Platform是在JVM上启动测试框架的基础,不仅支持Junit自制的测试引擎,其他测试引擎也都可以接入。
JUnit Jupiter: JUnit Jupiter提供了JUnit5的新的编程模型,是JUnit5新特性的核心。内部 包含了一个测试引擎,用于在Junit Platform上运行。
JUnit Vintage: 由于JUint已经发展多年,为了照顾老的项目,JUnit Vintage提供了兼容JUnit4.x,Junit3.x的测试引擎。
2、JUnit5 使用添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
3、创建完成,默认结构
!!!import org.junit.jupiter.api.Test; //注意这里使用的是jupiter的Test注解!!不是junit中的test
@SpringBootTest//这个是可以启动整个项目进行测试
class XiaoKeWebAdminApplicationTests {
@Test//标记这个方法要被测试
void testFunc() {
System.out.println("Junit5测试开始...")
}
}
4、JUnit5常用注解
- @Transactional 标注测试方法,测试完成后自动回滚,在服务层测试时与数据库交互使用,可以回滚数据中的测试数据
- @Test :表示方法是测试方法。但是与JUnit4的@Test不同,他的职责非常单一不能声明任何属性,拓展的测试将会由Jupiter提供额外测试
- @ParameterizedTest :表示方法是参数化测试
- @RepeatedTest :表示方法可重复执行
- @DisplayName :为测试类或者测试方法设置展示名称
- @BeforeEach :表示在每个单元测试之前执行
- @AfterEach :表示在每个单元测试之后执行
- @BeforeAll :表示在所有单元测试之前执行
- @AfterAll :表示在所有单元测试之后执行
- @Tag :表示单元测试类别,类似于JUnit4中的@Categories
- @Disabled :表示测试类或测试方法不执行,类似于JUnit4中的@Ignore
- @Timeout :表示测试方法运行如果超过了指定时间将会返回错误
- @ExtendWith :为测试类或测试方法提供扩展类引用
5、断言(assertions)
断言(assertions)是测试方法中的核心部分,用来对测试需要满足的条件进行验证。这些断言方法都是 org.junit.jupiter.api.Assertions 的静态方法
assertEquals 判断两个对象或两个原始类型是否相等
---
assertNotEquals 判断两个对象或两个原始类型是否不相等
---
assertSame 判断两个对象引用是否指向同一个对象
---
assertNotSame 判断两个对象引用是否指向不同的对象
---
assertTrue 判断给定的布尔值是否为 true
---
assertFalse 判断给定的布尔值是否为 false
---
assertNull 判断给定的对象引用是否为 null
---
assertNotNull 判断给定的对象引用是否不为 null
---
assertArrayEquals 方法来判断两个对象或原始类型的数组是否相等
---
assertAll 方法接受多个 org.junit.jupiter.api.Executable 函数式接口的实例作为要验证的断言,可以通过 lambda 表达式很容易的提供这些断言
---
Assertions.assertThrows() ,配合函数式编程就可以进行使用。
---
Assertions.assertTimeout() 为测试方法设置了超时时间
6、前置条件(assumptions)
JUnit 5 中的前置条件(assumptions【假设】)类似于断言,不同之处在于不满足的断言会使得测试方法失败,而不满足的前置条件只会使得测试方法的执行终止。前置条件可以看成是测试方法执行的前提,当该前提不满足时,就没有继续执行的必要。
@DisplayName("前置条件测试类")
public class AssumptionsTest {
private final String environment = "TEST";
@Test
@DisplayName("true or false")
public void simpleAssume() {
assumeTrue(Objects.equals(this.environment, "TEST"));
assumeFalse(() -> Objects.equals(this.environment, "PROD"));
}
@Test
@DisplayName("assumingthat ")
public void assumeThenDo() {
assumingThat(
Objects.equals(this.environment, "TEST"),
() -> System.out.println("In TEST")
);
}
}
assumeTrue 和 assumFalse 确保给定的条件为 true 或 false,不满足条件会使得测试执行终止。
assumingThat 的参数是表示条件的布尔值和对应的 Executable 接口的实现对象。只有条件满足时,Executable 对象才会被执行;当条件不满足时,测试执行并不会终止。
7、参数化测试
参数化测试是JUnit5很重要的一个新特性,它使得用不同的参数多次运行测试成为了可能,也为我们的单元测试带来许多便利。
利用@ValueSource等注解,指定入参,我们将可以使用不同的参数进行多次单元测试,而不需要每新增一个参数就新增一个单元测试,省去了很多冗余代码。
- @ValueSource: 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型
- @NullSource: 表示为参数化测试提供一个null的入参
- @EnumSource: 表示为参数化测试提供一个枚举入参
- @CsvFileSource:表示读取指定CSV文件内容作为参数化测试入参
- @MethodSource:表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)
8、service层测试(和数据库交互)
服务层一般是要和数据库进行交互的,可以通过以下两个注解起到回滚数据的功能
-
//@Transactional
-
//@Rollback()
import cn.hutool.core.lang.Assert;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest//测试服务层必须要有这个,可以测试整个springboot工程
class XiaokeApplicationTests {
@Autowired
private TestService testService;//引入这个服务
@Test
//@Transactional
//@Rollback(true) ,true回滚,false不回滚
void insertTest() {
User user = new User();
user.setId("100");
user.setName("xiaoke");
user.setAge(30);
testService.insert(user);
}
}
9、controller层测试(模拟发送请求)
mport org.junit.Assert;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
@SpringBootTest
//测试环境使用,用来表示测试环境使用的ApplicationContext将是WebApplicationContext类型的
@WebAppConfiguration
class XiaokeApplicationTests {
@Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
@BeforeEach
public void setUp() throws Exception {
// 初始化MockMvc
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
/**
* 1、mockMvc.perform执行一个请求。
* 2、MockMvcRequestBuilders.get("XXX")或者post()构造一个请求。
* 3、ResultActions.param添加请求传值
* 4、ResultActions.accept(MediaType.TEXT_HTML_VALUE))设置返回类型
* 5、ResultActions.andExpect添加执行完成后的断言。
* 6、ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情
* 比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息。
* 7、ResultActions.andReturn表示执行完成后返回相应的结果。
*/
@Test
void contextLoads() throws Exception {
MvcResult mvcResult = mockMvc
.perform(MockMvcRequestBuilders
.get("/test")
//.param("name", "xiaoke") 如果是post方式,则要添加param,构造body
.accept(MediaType.APPLICATION_JSON))
//等同于Assert.assertEquals(200,status);
.andExpect(MockMvcResultMatchers.status().isOk())
//等同于 Assert.assertEquals("hello world!",content);
.andExpect(MockMvcResultMatchers.content().string("hello test!"))
.andDo(MockMvcResultHandlers.print())
.andReturn();
//得到返回代码
int status = mvcResult.getResponse().getStatus();
//得到返回结果
String content = mvcResult.getResponse().getContentAsString();
//断言,判断返回代码是否正确
Assert.assertEquals(200, status);
//断言,判断返回的值是否正确
Assert.assertEquals("hello test!", content);
}
}