在 Spring Boot 1.4 MVC 测试中使用 @WebMvcTest 设置 MockMvc
Posted
技术标签:
【中文标题】在 Spring Boot 1.4 MVC 测试中使用 @WebMvcTest 设置 MockMvc【英文标题】:Setting up MockMvc with @WebMvcTest in Spring Boot 1.4 MVC Testing 【发布时间】:2016-11-05 05:00:39 【问题描述】:我几乎没有工作代码可以使用新的 Spring Boot 1.4 @WebMvcTest
以不同的方式设置 MockMVc
。我了解standaloneSetup 方法。我想知道的是设置MockMvc
到WebApplicationContext
和自动装配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();
所以我的问题是:
为什么 Code sn-p 1 没有报告 401 Unauthorized 状态代码错误,而自动接线 MockMvc
报告了。还要重申官方文档所说的 默认情况下,使用 @WebMvcTest 注释的测试也将自动配置 Spring Security 和 MockMvc。 但是,在这种情况下,@WebMvcTest
似乎与自动配置无关Spring Security(因为代码片段 1 通过没有任何 401 错误)。最后归结为我如何设置MockMvc
。我说的对吗?
这两种方法的区别/目标是什么?
通过@AutoConfigureMockMvc(secure=false)
禁用安全性与通过@WebMvcTest(controllers = IndexController.class, secure = false)
禁用安全性有何不同。哪个是首选方法或何时(或在何处)使用它们?
【问题讨论】:
【参考方案1】:我不知道这是否正确,但我可以使用下面的方法禁用配置类
@WebMvcTest(ProductController.class)
@ContextConfiguration(classes = ProductController.class)
public class ProductControllerTest
@Autowired
private MockMvc mockMvc;
@MockBean
private ProductService productServiceMock;
【讨论】:
【参考方案2】:我也遇到过类似的问题。 @WebMvcTest
自动使用基本身份验证配置 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")));
【讨论】:
【参考方案3】:我不确定这是否直接相关,但是有一个 outstanding bug,如果使用 spring boot 和 @WebMvcTest
,您的自定义 @EnableWebSecurity
配置类将被忽略。错误报告中提到了几个解决方法。我正在使用:
@WebMvcTest(includeFilters = @Filter(classes = EnableWebSecurity.class))
【讨论】:
【参考方案4】:根据github中的这个问题
https://github.com/spring-projects/spring-boot/issues/5476
@WebMvcTest 默认自动配置,当 spring-security-test 在类路径中时进行基本身份验证
回答您的问题:
-
在代码 sn-p 1 中,您没有在测试类中注入 MockMvc,您应该在 setup 方法的 builder 中添加 .apply(springSecurity()),因此 spring 将使用基本配置(不是您的自定义安全配置(如果有)
这两种方法的作用基本相同,不同之处在于第二种方法已经在 MockMvc 中提供了基本身份验证,这就是您必须使用 secure=false 的原因
来自文档:
默认情况下,使用 @WebMvcTest 注释的测试也会自动配置 Spring Security 和 MockMvc(包括对 htmlUnit WebClient 的支持 和 Selenium WebDriver)。为了更细粒度地控制 MockMVC 可以使用@AutoConfigureMockMvc 注解。
【讨论】:
以上是关于在 Spring Boot 1.4 MVC 测试中使用 @WebMvcTest 设置 MockMvc的主要内容,如果未能解决你的问题,请参考以下文章
使用 h2database 进行 Spring boot 1.4 测试(在每个测试中回滚)