Setting up MockMvc with @WebMvcTest in Spring Boot 1.4 MVC Testing

user2693135 picture user2693135 · Jul 3, 2016 · Viewed 23.5k times · Source

I have few working code to set up MockMVc in different ways with the new Spring Boot 1.4 @WebMvcTest. I understand the standaloneSetup approach. What I want to know is the difference between setting up MockMvc through WebApplicationContext and by autowiring MockMvc.

Code Snippet 1: MockMvc through WebApplicationContext Setup

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = ProductController.class)
public class ProductControllerTest {

private MockMvc mockMvc;

@Autowired
private WebApplicationContext webApplicationContext;

@MockBean
private ProductService productServiceMock;

@Before
public void setUp() {
     mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}

@Test
public void testShowProduct() throws Exception {      

    Product product1 = new Product();
    /*Code to initialize product1*/

    when(productServiceMock.getProductById(1)).thenReturn(product1);

    MvcResult result = mockMvc.perform(get("/product/{id}/", 1))
            .andExpect(status().isOk())
            /*Other expectations*/
            .andReturn();
  }
}

As per WebMvcTest API documentation, By default, tests annotated with @WebMvcTest will also auto-configure Spring Security and MockMvc. So, I expected a 401 Unauthorized status code here, but the test passes with a 200 status code.

Next, I tried auto wiring MockMvc, but the test fails with 401 Unauthorized status code, unless I add @AutoConfigureMockMvc(secure=false) or update the @WebMvcTest annotation to disable security:

@WebMvcTest(controllers = IndexController.class, secure = false)


Following is the code that passes ONLY AFTER explicitly disabling security.

Code Snippet 2: MockMvc through Autowiring

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = ProductController.class)
@AutoConfigureMockMvc(secure=false)
public class ProductControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private WebApplicationContext webApplicationContext;
@MockBean
private ProductService productServiceMock;

@Test
public void testShowProduct() throws Exception {      

    Product product1 = new Product();
    /*Code to initialize product1*/

    when(productServiceMock.getProductById(1)).thenReturn(product1);

    MvcResult result = mockMvc.perform(get("/product/{id}/", 1))
            .andExpect(status().isOk())
            /*Other expectations*/
            .andReturn();
  }
}

So my questions are:

  1. Why didn't Code snippet 1 report a a 401 Unauthorized status code error while auto wiring MockMvc did. Also reiterating what the official doc says By default, tests annotated with @WebMvcTest will also auto-configure Spring Security and MockMvc. But, in this case it appears @WebMvcTest has nothing to do with auto-configuring Spring Security (Because Code Snippet 1 passes without any 401 error). It finally boils down to how I set up the MockMvc. Am I correct here?

  2. What are the differences/objectives between/of both the approaches?

  3. How does disabling security via @AutoConfigureMockMvc(secure=false) differs from doing through @WebMvcTest(controllers = IndexController.class, secure = false). Which one is the preferred approached or when (or where) to use them?

Answer

UmitYeldan picture UmitYeldan · Sep 10, 2016

I also come across similar problem. @WebMvcTest auto configures Spring Security with basic auth but I have a WebSecurityConfig class that extends WebSecurityConfigurerAdapter. In this class I disabled basic auth and configured token base security. That means WebSecurityConfig class is not used to configure Spring Security.

To resolve the problem, I added @ContextConfiguration to my unit test class and added mocks of dependencies of WebSecurityConfig class.

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = CategoryRestService.class)
@ContextConfiguration(classes = {MjApplication.class, WebSecurityConfig.class})
public class CategoryRestServiceTest {
    
    @MockBean
    private CategoryRepository repository;
    
    @MockBean
    CurrentUserDetailsService currentUserDetailsService;
    
    @MockBean
    TokenAuthProvider tokenAuthProvider;
    
    @Autowired
    MockMvc mockMvc;
    
    private MediaType contentType = new    MediaType(MediaType.APPLICATION_JSON.getType(),
            MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
    
    
    @Test
    public void getCategories() throws Exception {
        Category category1 = new Category();
        category1.setName("Test Category 1");
        category1.setId(1L);
        Category category2 = new Category();
        category2.setName("Test Category 2");
        category2.setId(2L);
        List<Category> categoryList = new ArrayList<Category>();
        categoryList.add(category1);
        categoryList.add(category2);
        given(this.repository.findAll())
        .willReturn(categoryList);
        mockMvc.perform(get("/public/rest/category"))
        .andExpect(status().isOk())
        .andExpect(content().contentType(contentType))
        .andExpect(jsonPath("$[0].id", is(1)))
        .andExpect(jsonPath("$[0].name", is("Test Category 1")))
        .andExpect(jsonPath("$[1].id", is(2)))
        .andExpect(jsonPath("$[1].name", is("Test Category 2")));
    }

}