单元测试
junit4 与junit5的选择问题
1、问题描述
在使用SpringBoot2来进行单元测试时,发现不需要加@runWith注解,其次当测试类路径与boot启动类不是相同包或其子包时,必须指定启动类的 .clas
2、产生原因
- 首先针对这个问题,我们所使用的是springboot 2、值得注意的是这个版本对于junit做了较大的改动,移除了对junit3和4的支持,使用jupiter测试引擎替换了原来的junit4作为核心模块提供测试服务,这也是为什么昨天测试中最开始自动引入的是org.junit.jupiter的原因。
- 添加依赖spring_boot_starter_test后,可以在内部看到自带了jupiter测试核心模块,这是pom里最开始没有看到老版本junit4依赖的原因。
3、版本区别
- junit4,也就是老版本在使用测试时,就需要像昨天后边一段一样,手动添加依赖支持,不过使用junit4需要使用@springbootTest和@runWith注解,其中runWith指向使用spring的测试模块。
junit5(jupiter测试引擎)不再支持junit4(vintage测试引擎),在使用时自然不再需要spring来提供了,即不需要再使用@runWith注解。
- 最后一个小问题就是,@springbootTest属性的问题。分两种情况:
1)测试类所在目录跟boot启动类目录相同或者是子目录的情况下,在测试时可以自动找到启动类。即注解中不需要指定启动类的字节码。
2)不是相同目录或者子目录时,就需要指定启动类的字节码了.,@springbootTest(clases=✘✘✘.class)
4、ExtendWith(SpringExtension.class)、@DisplayName(“Base Test”)意义
Junit5:demo
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = Application.class)
@DisplayName("Base Test")
public class BaseTest {
@Autowired
protected WebApplicationContext wac;
protected MockMvc mockMvc;
@org.junit.jupiter.api.BeforeEach
public void setUp() throws Exception {
mockMvc = webAppContextSetup(wac).build();
}
}
Spring 2.4.0 的测试引擎 junit-vintage
从 Spring Boot 2.4.0 的测试引擎已经使用 Junit 5 的测试了。
因此测试引擎不再需要 exclude junit-vintage 到 Spring Boot 的测试依赖了。
2.4.0 的测试实例应该使用下面的依赖配置:
<!-- TESTS -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
###说明
单元测试 注入方法 @ExtendWith
当涉及Spring时:
如果您想在测试中使用Spring测试框架功能(例如)@MockBean,则必须使用@ExtendWith(SpringExtension.class)。它取代了不推荐使用的JUnit4@RunWith(SpringJUnit4ClassRunner.class)
当不涉及Spring时:
例如,如果您只想涉及Mockito而不必涉及Spring,那么当您只想使用@Mock/ @InjectMocks批注时,您就想使用@ExtendWith(MockitoExtension.class),因为它不会加载到很多不需要的Spring东西中。它替换了不推荐使用的JUnit4 @RunWith(MockitoJUnitRunner.class)。
要回答您的问题:
是的,您可以只使用@ExtendWith(SpringExtension.class),但是如果您的测试中没有涉及Spring测试框架功能,那么您可能只想使用@ExtendWith(MockitoExtension.class)
在升级
spring-boot-starter-parent版本到 2.4.0 以后JUnit 5 测试 Spring 引擎的时候提示 junit-vintage 错误
TestEngine with ID 'junit-vintage' failed to discover tests” with Spring
junit-vintage 是 Junit 4 中使用的引擎,如果你的项目使用了 Junit 5 的话,你需要在 spring-boot-starter-test 中将 JUnit 4 的引擎从测试中删除。
<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>
Junit5生命周期注解 和 DisplayName 注解
- Constructor:在 Juni5 中,每一个通过 @Test注解 调用的测试方法都是一个独立的实例,因此每一个测试方法都会调用构造函数:
- @BeforeEach & @AfterEach 如注释的名称,在每一次 @Test 调用之前都会运行一次。
- @BeforeAll 与 @AfterAll 会在所有测试执行之前执行,值得注意的是,@BeforeAll 与 @AfterAll 都是静态方法:
- @DisplayName注解 用来提供个性化的名称,可以用于测试类与测试方法,用法大同小异。@DisplayName注解可以用来输出普通文字、特殊文字以及 emoji,能为测试提供更有意义的输出:
package com.example;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@DisplayName("App Test with spaces")
public class AppTest
{
@Test
@DisplayName("中文对应")
public void testOne()
{
System.out.println("testOne");
}
@Test
@DisplayName("w(゚Д゚)w")
public void testTwo()
{
System.out.println("testOne");
}
@Test
@DisplayName("✔️")
public void testThree()
{
System.out.println("testOne");
}
@Test
@DisplayName("❌")
public void testFour()
{
System.out.println("testOne");
}
}
注*:不同 编辑器/IDE 的配置不太一样,有的 IDE 可能不会显示原本的类/方法名,而是直接显示 @DisplayName 中的输入。