Spring Boot Testing - MockMvc

Use Spring’s @MockMvc to test the controller. Do not start the server at all.

  1. Spring handles the incoming HTTP request and hands it off to your controller.
  2. With this approach, the code in the controller will be called in exactly the same way as if it were processing a real HTTP request but without the cost of starting the server.
  3. Use Spring’s MockMvc and ask for that to be injected for you by using the @AutoConfigureMockMvc annotation on the test case.
  4. The full Spring application context is started but without the server.
  5. MockMvc provides support for Spring MVC testing. It encapsulates all web application beans and makes them available for testing.
package com.example.weblayertestingforspringbootapp;

import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

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;

@SpringBootTest
@AutoConfigureMockMvc
public class TestingWebApplicationTest {

   @Autowired
   private MockMvc mockMvc;

   @Test
   public void shouldReturnDefaultMessage() throws Exception {
           this.mockMvc
               .perform(get("/"))
               .andDo(print())
               .andExpect(status().isOk())
               .andExpect(content().string(containsString("Hello, World")));
   }
}

Verify Response Body

@Test
public void givenGreetURI_whenMockMVC_thenVerifyResponse() {
    MvcResult mvcResult = this.mockMvc.perform(get("/greet"))
      .andDo(print()).andExpect(status().isOk())
      .andExpect(jsonPath("$.message").value("Hello World!!!"))
      .andReturn();

    assertEquals("application/json;charset=UTF-8", mvcResult.getResponse().getContentType());
}

Send GET Request With Path Variable

@Test
public void givenGreetURIWithPathVariable_whenMockMVC_thenResponseOK() {
    this.mockMvc
      .perform(get("/greetWithPathVariable/{name}", "John"))
      .andDo(print()).andExpect(status().isOk())
      .andExpect(content().contentType("application/json;charset=UTF-8"))
      .andExpect(jsonPath("$.message").value("Hello World John!!!"));
}

Send GET Request With Query Parameters

@Test
public void givenGreetURIWithQueryParameter_whenMockMVC_thenResponseOK() {
    this.mockMvc.perform(get("/greetWithQueryVariable")
      .param("name", "John Doe")).andDo(print()).andExpect(status().isOk())
      .andExpect(content().contentType("application/json;charset=UTF-8"))
      .andExpect(jsonPath("$.message").value("Hello World John Doe!!!"));
}

Send POST Request

@Test
public void givenGreetURIWithPost_whenMockMVC_thenVerifyResponse() {
    this.mockMvc.perform(post("/greetWithPost")).andDo(print())
      .andExpect(status().isOk()).andExpect(content()
      .contentType("application/json;charset=UTF-8"))
      .andExpect(jsonPath("$.message").value("Hello World!!!"));
}

Limitations of MockMvc

MockMvc provides an elegant and easy-to-use API to call web endpoints and to inspect and assert their response at the same time. Despite all its benefits, it has a few limitations.

First of all, it does use a subclass of the DispatcherServlet to handle test requests. To be more specific, the TestDispatcherServlet is responsible for calling controllers and performing all the familiar Spring magic.

The MockMvc class wraps this TestDispatcherServlet internally. So, every time we send a request using the perform() method, MockMvc will use the underlying TestDispatcherServlet directly. Therefore, no real network connections are made, and consequently, we won’t test the whole network stack while using MockMvc.

Also, because Spring prepares a fake web application context to mock the HTTP requests and responses, it may not support all the features of a full-blown Spring application.

For example, this mock setup doesn’t support HTTP redirections. This may not seem that significant at first. However, Spring Boot handles some errors by redirecting the current request to the /error endpoint. So, if we’re using the MockMvc, we may not be able to test some API failures.

Alternatives

  1. We can set up a more real application context and then use RestTemplate. See Spring Boot Testing - Integration tests for the Controller
  2. REST-assured: https://www.baeldung.com/rest-assured-tutorial

Reference

  1. https://spring.io/guides/gs/testing-web
  2. https://www.baeldung.com/integration-testing-in-spring