The introduction

This article discusses how to test against the Web layer of Spring Boot programs, implement test coverage of Web layer methods with the MockMvc API and common test libraries, and how to customize MockMvc.

The preparatory work

  • Java 8
  • Maven 3
  • Spring Boot 2.x.x

Create a New Spring Boot project and ensure that the project has the following Maven dependencies:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<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>
</dependency>
Copy the code

Configuration MockMvc

First, create a unit test class for the corresponding Controller class and use the @SpringBooTtest annotation to mark the test class to indicate that it is a Unit test class based on SpringBoot. When running the test class, the framework will create an application context. All configured beans in the application are created into the application context, such as some Service component or configuration component that the Controller class depends on. We then use the @AutoConfiguRemockMVC annotation to automate the MockMvc object that plays a key role in the Web layer test, through which we write the controller method to implement the test call.

@SpringBootTest
@AutoConfigureMockMvc
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;
    
    //....
}
Copy the code

In addition, before writing test methods, the official recommendation is to statically import the following classes for writing concise step methods and validation methods.

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
Copy the code

MockMvcRequestBuilder is used to build HTTP request data: parameters, methods, requests, etc. API calls adopt the builder pattern, worth learning; MockMvcResultMatcher is a match of information to the result of the response: status code, content, manner, etc. MockMvcResultHandlers is mainly used to indicate additional operations on results, such as printing, logging, etc.

Test the GET method

Start with the simplest, when we implement a GET method request interface access user information path:

http://localhost:8080/user/1
Copy the code

The test code to be implemented is as follows:

@Test
void should_get_user(a) throws Exception {
    mockMvc.perform(get("/user/{id}".1L))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.username").value("test"));
}
Copy the code

GET (String urlTemplate, Object… UriVar), you can see that if there are more than one parameter, the variable parameter list can be supplemented.

IsOk (); jsonPath(“$.username”).value(“test”); jsonPath(“$.username”).value(“test”); Here with the help of the MockMvcResultMatchers. JsonPath realize the json data extract, want to further study can search jsonPath.

This code completes the test access and assertion for a GET request, which raises the question of how to write a test if there are no parameters on the request path. Here we build a GET request with the following access path:

http://localhost:8080/user/getScore?id=1
Copy the code

For this kind of request to write test methods as follows, using MockHttpServletRequestBuilder. QueryParam added specific request parameter key-value pairs, if there are multiple request parameters, but also through additional chain call way.

@Test
void should_getScore(a) throws Exception {
    mockMvc.perform(get("/user/getScore").queryParam("id"."1"))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(content().string("100"));
}
Copy the code

Test the POST method

To explain in detail the test GET request methods, we will know how POST method under test, according to the POST request here carries the Content Type (the content-type) classification, there are three main types: Form submission format – Application/X-www-form-urlencoded, JSON data format -application/ JSON, file upload format -multipart/form-data, the corresponding test codes for each case are as follows:

Test the form submission POST method:

@Test
void should_login(a) throws Exception {
    mockMvc.perform(post("/user/login")
                    .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                    .content("username=test&password=pwd"))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.username").value("test"));
}
Copy the code

Test JSON data POST method:

@Test
void should_login2(a) throws Exception {
    mockMvc.perform(post("/user/login2")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content("{"username":"test","password":"pwd"}"))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.username").value("test"));
}
Copy the code

Test file upload POST method:

@Test
public void should_doc(a) throws Exception {
    mockMvc.perform(multipart("/doc")
                    .file("file"."ABC".getBytes("UTF-8")))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.data").value("ABC"));
}
Copy the code

As you can see from the above code, it’s pretty easy to write the same test code for various request methods. Let’s take a look at MockMvc further.

MockMvc advanced

The MockMvc we used earlier was injected by Spring for us. What if we want to customize MockMvc? MockMvcBuilders are provided to help us build a global MockMvc, and can be configured globally by default to define common operations such as printing results, assertion response codes, and so on, as shown in the following code example:

@SpringBootTest
class MockmvcDemoApplicationTests {

  private MockMvc mockMvc;

  @BeforeAll
  void setUp(WebApplicationContext wac) {
    this.mockMvc = MockMvcBuilders.webAppContextSetup(wac) .alwaysExpect(status().isOk()) .alwaysExpect(content().contentType(MediaType.APPLICATION_JSON)) .alwaysDo(print()) .build(); }}Copy the code

In addition, using @Springboottest will create a complete application context and load all the beans. If the application itself is large, it will take too long to start the test class. Is there any way to speed up the startup of the application when testing the Web layer?

In order to speed up the startup of the application when running test cases, a special annotation @webmvctest is provided to ensure that only the Web layer is initialized, not the entire application context, and a controller can be specified to achieve the initialization of only specific controllers and dependencies, greatly speeding up the operation of test cases.

@WebMvcTest(controllers = UserController.class)
class UserController2Test {

    @Autowired
    private MockMvc mockMvc;

} 
Copy the code

In addition to annotations, we can also inject and test individual controllers in API form, again with MockMvcBuilders, as shown in the following code:

class UserController3Test {
    MockMvc mockMvc;

    @BeforeEach
    void setUp(a) {
        this.mockMvc = MockMvcBuilders.standaloneSetup(newUserController()).build(); }}Copy the code

Note that using standaloneSetup does not read any configuration, much closer to the unit test for this controller class.

conclusion

Well, that’s all you need to know in this article about how Spring Boot can do a good job of testing the Web layer. As you can see, testing the Web layer is not very complicated and the related API is very readable. MockMvc is used to test the Web layer. The bottom layer does not actually access the interface through the network request, nor does it start the Web container. The bottom layer is really just a Mock implementation of the Servlet API, so it is quite different from traditional end-to-end integration testing. If you are doing simple integration tests and unit tests on your own Web layer code, you can refer to the methods described above.

Finally, you can refer to the official document address given at the end of this article for further information about MockMvc and implementing Web layer testing by Spring Boot. I hope this introduction will be helpful for daily development. In the future, I will continue to introduce articles about using Spring Boot to do testing. Stay tuned.

Refer to the reading

  • 3.7. MockMvc: docs. Spring. IO/spring – fram…
  • 26.3.13. Auto-configured Spring MVC Tests: doces.spring. IO /spring-boot…
  • Article code example project: github.com/developer-w…