user2693135
user2693135

Reputation: 1306

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

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?

Upvotes: 17

Views: 31111

Answers (4)

Bluezery
Bluezery

Reputation: 37

I don't know if this is correct way or not but I could disable configurations class by using like below

@WebMvcTest(ProductController.class)
@ContextConfiguration(classes = ProductController.class)
public class ProductControllerTest {
  @Autowired
  private MockMvc mockMvc;

  @MockBean
  private ProductService productServiceMock;

Upvotes: 0

UmitYeldan
UmitYeldan

Reputation: 182

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")));
    }

}

Upvotes: 11

djeikyb
djeikyb

Reputation: 4589

I'm not sure this is directly related, but there is an outstanding bug where, if using spring boot and @WebMvcTest, your custom @EnableWebSecurity config class will be ignored. A couple workarounds are mentioned in the bug report. I'm using:

@WebMvcTest(includeFilters = @Filter(classes = EnableWebSecurity.class))

Upvotes: 5

Marco Prado
Marco Prado

Reputation: 1298

According to this issue in github

https://github.com/spring-projects/spring-boot/issues/5476

@WebMvcTest auto configures by default, a basic auth when spring-security-test is in the class path

Answering your questions:

  1. In the code snippet 1, you not injected the MockMvc in your test class, you should add .apply(springSecurity()) in the builder on the setup method, so spring would use the basic configuration (not your custom security configuration if you have one)
  2. both approaches does basically the same thing, the difference is that the second comes with the basic auth already in the MockMvc, that is why you have to use the secure=false
  3. From the documentation:

By default, tests annotated with @WebMvcTest will also auto-configure Spring Security and MockMvc (include support for HtmlUnit WebClient and Selenium WebDriver). For more fine-grained control of MockMVC the @AutoConfigureMockMvc annotation can be used.

Upvotes: 6

Related Questions