Implementation of SpringBoot+JUnit5+MockMvc+Mockito Unit Test

  • 2021-11-13 01:46:10
  • OfStack

Directory version Project structure EchoServiceImpl
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 JUnit4

Since 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.2

SpringBoot 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.


Related articles: