This paper introduces the unit test implementation scheme of Spring Boot 2 based on JUnit 5.
catalogue
- brief introduction
- Differences between JUnit 4 and JUnit 5
- Ignore test case execution
- RunWith configuration
- @Before, @BeforeClass, @After, @AfterClass replaced
- development environment
- Example
brief introduction
Spring boot version 2.2.0 began to introduce JUnit 5 as the default library for unit tests. Before spring boot version 2.2.0, spring boot starter test included JUnit 4 dependencies. After spring boot version 2.2.0, Junit Jupiter was replaced.
Differences between JUnit 4 and JUnit 5
1. ignore test case execution
JUnit 4:
@Test @Ignore public void testMethod() { // ... }
JUnit 5:
@Test @Disabled("explanation") public void testMethod() { // ... }
2. RunWith configuration
JUnit 4:
@RunWith(SpringRunner.class) @SpringBootTest public class ApplicationTests { @Test public void contextLoads() { } }
JUnit 5:
@ExtendWith(SpringExtension.class) @SpringBootTest public class ApplicationTests { @Test public void contextLoads() { } }
3. @Before, @BeforeClass, @After, @AfterClass are replaced
- @BeforeEach replace @Before
- @BeforeAll replace @BeforeClass
- @AfterEach replace @After
- @AfterAll replace @AfterClass
development environment
- JDK 8
Example
-
To create a Spring Boot project, refer to: IntelliJ IDEA creates a Spring Boot project.
-
Add spring boot starter web dependency, and finally pom The XML is as follows.
<?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.2.6.RELEASE</version> <relativePath/> </parent> <groupId>tutorial.spring.boot</groupId> <artifactId>spring-boot-junit5</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-boot-junit5</name> <description>Demo project for Spring Boot Unit Test with JUnit 5</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.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> </plugin> </plugins> </build> </project>
- After the project is created, a test class is automatically generated.
package tutorial.spring.boot.junit5; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class SpringBootJunit5ApplicationTests { @Test void contextLoads() { } }
The purpose of this test class is to check whether the application context can be started normally@ The SpringBootTest annotation tells Spring Boot to find the main configuration class with the @SpringBootApplication annotation and use it to start the Spring application context.
- Supplement the application logic code to be tested
4.1 Define Service layer interface
package tutorial.spring.boot.junit5.service; public interface HelloService { String hello(String name); }
4.2 Define Controller layer
package tutorial.spring.boot.junit5.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import tutorial.spring.boot.junit5.service.HelloService; @RestController public class HelloController { private final HelloService helloService; public HelloController(HelloService helloService) { this.helloService = helloService; } @GetMapping("/hello/{name}") public String hello(@PathVariable("name") String name) { return helloService.hello(name); } }
4.3 Define the Service layer implementation
package tutorial.spring.boot.junit5.service.impl; import org.springframework.stereotype.Service; import tutorial.spring.boot.junit5.service.HelloService; @Service public class HelloServiceImpl implements HelloService { @Override public String hello(String name) { return "Hello, " + name; } }
- Write unit tests that send HTTP requests.
package tutorial.spring.boot.junit5; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.web.server.LocalServerPort; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class HttpRequestTest { @LocalServerPort private int port; @Autowired private TestRestTemplate restTemplate; @Test public void testHello() { String requestResult = this.restTemplate.getForObject("http://127.0.0.1:" + port + "/hello/spring", String.class); Assertions.assertThat(requestResult).contains("Hello, spring"); } }
Description:
- Webenvironment = springboottest Webenvironment Random_ Port uses a local random port to start the service;
- @LocalServerPort is equivalent to @Value("${local.server.port}");
- After configuring the webEnvironment, Spring Boot will automatically provide a TestRestTemplate instance that can be used to send HTTP requests.
- In addition to using the TestRestTemplate instance to send HTTP requests, you can also use org Springframework Test Web Servlet Mockmvc performs similar functions with the following codes:
package tutorial.spring.boot.junit5.controller; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; @SpringBootTest @AutoConfigureMockMvc public class HelloControllerTest { @Autowired private HelloController helloController; @Autowired private MockMvc mockMvc; @Test public void testNotNull() { Assertions.assertThat(helloController).isNotNull(); } @Test public void testHello() throws Exception { this.mockMvc.perform(MockMvcRequestBuilders.get("/hello/spring")) .andDo(MockMvcResultHandlers.print()) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.content().string("Hello, spring")); } }
The above test method belongs to the overall test, that is, the application context is all started. There is also a layered test method, such as testing only the Controller layer.
- Layered test.
package tutorial.spring.boot.junit5.controller; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.web.servlet.MockMvc; 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 tutorial.spring.boot.junit5.service.HelloService; @WebMvcTest public class HelloControllerTest { @Autowired private HelloController helloController; @Autowired private MockMvc mockMvc; @MockBean private HelloService helloService; @Test public void testNotNull() { Assertions.assertThat(helloController).isNotNull(); } @Test public void testHello() throws Exception { Mockito.when(helloService.hello(Mockito.anyString())).thenReturn("Mock hello"); this.mockMvc.perform(MockMvcRequestBuilders.get("/hello/spring")) .andDo(MockMvcResultHandlers.print()) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.content().string("Mock hello")); } }
Description:
- @The WebMvcTest annotation tells Spring Boot to instantiate only the Controller layer without instantiating the overall context. It can also further specify to instantiate only a certain instance of the Controller layer: @WebMvcTest(HelloController.class);
- Because only the Controller layer is instantiated, the dependent Service layer instances need to be created through @MockBean, and the return results of method calls of the Service layer instances from Mockito should be specified through the method of Mockito.