Implementation of SpringBoot+JUnit5+MockMvc+Mockito Unit Test
- 2021-11-13 01:46:10
- OfStack
EchoControllerNoMockitoTest
EchoControllerMockTest
Today, let's talk about how to integrate Junit5, MockMvc and Mocktio in SpringBoot. Junit5 is the most widely used test framework in Java stack, and Junit4 dominates the list 1 degrees.
After upgrading to Junit5, in addition to adding many features of Java8, many functional enhancements have been made, the structure has been optimized and adjusted, and many different modules have been split, which can be introduced as needed, such as:
JUnit Platform-Start the test framework on JVM JUnit Jupiter-Writing tests and extensions in JUnit5 JUnit Vintage-Provides a test engine running based on JUnit3 and JUnit4Since SpringBoot 2.2. 0, Junit 5 has become the default version of Junit. With JUnit Vintage, the cost of migrating from Junit4 to Junit5 is extremely low. So this article starts directly with Junit5.
Version
Let's talk about the version first, in order to avoid all kinds of strange problems due to version differences:
JDK: jdk8 (Minor version can be ignored) SpringBoot: 2.5. 2 Inheriting spring-boot-starter-parent Dependence on spring-boot-starter-web Dependence on spring-boot-starter-test JUnit: 5.7. 2 Mockito: 3.9. 0 hamcrest: 2.2SpringBoot has the advantage of inheriting spring-boot-starter-parent or introducing spring-boot-pom-dependencies and then adding spring-boot-starter-test dependency. The POM is defined 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.5.2</version>
</parent>
<groupId>cn.howardliu.effective.spring</groupId>
<artifactId>springboot-junit5-mockito</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-junit5-mockio</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</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>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Because we inherit spring-boot-starter-parent, we rely on spring-boot-starter-test without writing a specific version, and can directly integrate the parent version definition. Among them, spring-boot-starter-web is an web container for providing REST API, spring-boot-starter-test can provide various test frameworks, and spring-boot-maven-plugin is a plug-in that packages SpringBoot applications into executable jar.
Project structure
Because this is an DEMO example, we implement an Echo interface that can receive request parameters and return processed strings. By convention, we use the universal Hello, World! .
Our project structure is as follows:
-- pom.xml
Off- src
-- main
The -- java
The The Off- cn
The The Off- howardliu
The The Off- effective
The The Off- spring
The The Off- springbootjunit5mockio
The The -- SpringbootJunit5MockioApplication.java
The The -- controller
The The The Off- EchoController.java
The The Off- service
The The -- EchoService.java
The The Off- impl
The The Off- EchoServiceImpl.java
The Off- resources
The Off- application.yaml
Off- test
Off- java
Off- cn
Off- howardliu
Off- effective
Off- spring
Off- springbootjunit5mockio
Off- controller
-- EchoControllerMockTest.java
Off- EchoControllerNoMockitoTest.java
SpringbootJunit5MockioApplication: SpringBoot Application Startup Portal
EchoController: Interface Definition
EchoService: Implementing Business Logic Interfaces
EchoServiceImpl: Interface Implementation
EchoControllerMockTest: Implementation using Mock proxy EchoService
EchoControllerNoMockitoTest: Direct Test Interface Implementation
EchoServiceImpl
Let's look at the implementation of EchoService, which will be the core implementation of our DEMO:
@Service
public class EchoServiceImpl implements EchoService {
@Override
public String echo(String foo) {
return "Hello, " + foo;
}
}
EchoControllerNoMockitoTest
We first use Junit5+MockMvc to implement a common call to the Controller interface, with the following code:
@SpringBootTest(classes = SpringbootJunit5MockioApplication.class)
@AutoConfigureMockMvc
class EchoControllerNoMockitoTest {
@Autowired
private MockMvc mockMvc;
@Test
void echo() throws Exception {
final String result = mockMvc.perform(
MockMvcRequestBuilders.get("/echo/")
.param("name", " Look at the mountains ")
)
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print())
.andReturn()
.getResponse()
.getContentAsString(StandardCharsets.UTF_8);
Assertions.assertEquals("Hello, Look at the mountains ", result);
}
}
We define this as a test case for an SpringBoot application through the SpringBootTest annotation, and then launch the test container through AutoConfigureMockMvc. In this way, the MockMvc instance can be directly injected to test the Controller interface.
One point to note here is that many tutorials on the Internet will make it unnecessary to write such a annotation as @ ExtendWith ({SpringExtension. class}). From the source code, we know that ExtendWith has been added to the SpringBootTest annotation.
EchoControllerMockTest
In this test case, we proxy the echo method of EchoService through the Mockito component, with the following code:
@SpringBootTest(classes = SpringbootJunit5MockioApplication.class)
@ExtendWith(MockitoExtension.class)
@AutoConfigureMockMvc
class EchoControllerMockTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private EchoService echoService;
@BeforeEach
void setUp() {
Mockito.when(echoService.echo(Mockito.any()))
.thenReturn(" Look at the mountain and say: " + System.currentTimeMillis());
}
@Test
void echo() throws Exception {
final String result = mockMvc.perform(
MockMvcRequestBuilders.get("/echo/")
.param("name", " A cottage looking at the mountains ")
)
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print())
.andReturn()
.getResponse()
.getContentAsString(StandardCharsets.UTF_8);
Assertions.assertTrue(result.startsWith(" Look at the mountains "));
}
}
In this example, we need to pay attention to the @ ExtendWith (MockitoExtension. class) annotation, which was used to introduce MockBean, and we intercepted the echo method to make it return the response result we defined. This approach is so that in multi-system or multi-functional testing, there is no need to really call the interface.
For example, we need to get the user's mobile phone number, and usually check whether the user is logged in in the interface, so we can use Mockito's capability proxy login authentication, so that the result is always true.