在Spring Boot 1.4 MVC测试中使用@WebMvcTest设置MockMvc

时间:2016-07-03 08:33:48

标签: spring-mvc spring-boot spring-mvc-test

我使用新的Spring Boot 1.4 MockMVc以不同的方式设置@WebMvcTest的工作代码很少。我理解standaloneSetup方法。我想知道的是设置MockMvcWebApplicationContext和自动装配MockMvc之间的区别。

代码段1:通过WebApplicationContext设置MockMvc

@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();
  }
}

根据WebMvcTest API文档,默认情况下,使用@WebMvcTest注释的测试也会自动配置Spring Security和MockMvc 。所以,我希望这里有一个401 Unauthorized状态代码,但测试通过200状态代码。

接下来,我尝试了自动连接MockMvc,但测试失败并显示401 Unauthorized状态代码,除非我添加@AutoConfigureMockMvc(secure=false)或更新@WebMvcTest注释以禁用安全性:

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


以下是仅在明确禁用安全性后才传递的代码。

代码段2:通过自动装配模拟MockMvc

@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();
  }
}

所以我的问题是:

  1. 为什么没有代码段1在自动连接MockMvc时报告401未授权状态代码错误。同样重申官方文档所说的默认情况下,使用@WebMvcTest注释的测试也会自动配置Spring Security和MockMvc。但是,在这种情况下,它显示@WebMvcTest与auto无关 - 配置Spring安全性(因为代码片段1通过而没有任何401错误)。它最终归结为我如何设置MockMvc。我在这里纠正吗?

  2. 两种方法之间的差异/目标是什么?

  3. 如何通过@AutoConfigureMockMvc(secure=false)禁用安全性与执行@WebMvcTest(controllers = IndexController.class, secure = false)的方式不同。哪一个是接近的,或者何时(或在何处)使用它们?

4 个答案:

答案 0 :(得分:8)

我也遇到过类似的问题。 @WebMvcTest auto使用基本身份验证配置Spring Security,但我有一个扩展WebSecurityConfigurerAdapter的WebSecurityConfig类。在这个类中,我禁用了基本身份验证和配置的令牌库安全性。这意味着WebSecurityConfig类不用于配置Spring Security。

为了解决这个问题,我将@ContextConfiguration添加到我的单元测试类中,并添加了WebSecurityConfig类的依赖项的模拟。

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

}

答案 1 :(得分:6)

根据github中的这个问题

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

@WebMvcTest默认情况下自动配置,当spring-security-test在类路径中时是一个基本的auth

回答你的问题:

  1. 在代码片段1中,您没有在测试类中注入MockMvc,您应该在构建方法的构建器中添加.apply(springSecurity()),因此spring将使用基本配置(而不是您的自定义安全配置)如果你有一个)
  2. 两种方法基本上都是一样的,区别在于第二种方法已经在MockMvc中提供了基本的auth,这就是为什么你必须使用secure = false
  3. 来自文档:
  4.   

    默认情况下,使用@WebMvcTest注释的测试也会自动配置   Spring Security和MockMvc(包括对HtmlUnit WebClient的支持   和Selenium WebDriver)。对于MockMVC的更精细控制   可以使用@AutoConfigureMockMvc注释。

答案 2 :(得分:3)

我不确定这是否直接相关,但有一个outstanding bug,如果使用spring boot和@WebMvcTest,您的自定义@EnableWebSecurity配置类将被忽略。错误报告中提到了几种解决方法。我正在使用:

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

答案 3 :(得分:0)

我不知道这是否正确,但我可以使用下面的方法禁用配置类

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

  @MockBean
  private ProductService productServiceMock;