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:
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?
What are the differences/objectives between/of both the approaches?
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?
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")));
}
}