本文使用的是Spring Boot 5.7.0 version。
1.
@WebMvcTest
注解
官网关于Spring MVC相关的文档: https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.testing.spring-boot-applications.spring-mvc-tests
用位于
spring-boot-test-autoconfigure
包中的
@WebMvcTest
注解来测试Spring MVC Controller的行为(主要是会为我们自动注入一个
MockMvc
对象,Spring MVC Server端测试的主要类,
MockMvc
位于
spring-test
包中)。
@WebMvcTest
注解通常需要传入目标Controller类,如
@WebMvcTest(BookController.class)
,即它会自动扫描BookController类以及注入Spring MVC相关的配置,扫描的配置列举如下:
@Controller, @ControllerAdvice, @JsonComponent, Converter, GenericConverter, Filter, HandlerInterceptor, WebMvcConfigurer, WebMvcRegistrations, and HandlerMethodArgumentResolver
如果BookController中有BookService,
@WebMvcTest
并不会自动帮我们装配,需要我们通过@MockBean来声明一个Mock对象,并且使用Mockito的when/then/...来Mock BookService的行为(后面有示例)。
【一些其它的点】
@Component
注解的配置以及
@ConfigurationProperties
并不会被扫描。
@EnableConfigurationProperties
来开始对
@ConfigurationProperties
的扫描。
@WebMvcTest
默认开启的auto-configuration,可以在
页面
中找到。
@Import
。
@SpringBootTest
这样的全局扫描,也可以使用
@AutoConfigureMockMvc
来代替
@WebMvcTest
,并且需要加上
@SpringBootTest
注解。
1.1
@WebMvcTest
vs
@SpringBootTest
在项目中新建一个Configuration:
@Configuration
public class BookConfiguration {
@Bean
public BookDomain bookDomain() {
return new BookDomain(1001, "test bean");
@SpringBootTest
会自动装配@SpringBootApplication类所有的包以及子包下所有的Bean:
@SpringBootTest
public class BookConfigurationTest {
@Autowired
private BookDomain bookDomain;
@Test
public void beanTest() {
Assertions.assertEquals(1001, bookDomain.getId());
Assertions.assertEquals("test bean", bookDomain.getName());
我们使用@WebMvcTest
测试目标Controller:BookController,目的是为了测试会不会自动扫描@Configuration配置:
@WebMvcTest(BookController.class)
public class BookControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
BookService bookService;
@Autowired
private BookDomain bookDomain;
@Test
public void test() {
会报错:> nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.domain.BookDomain' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
即@WebMvcTest
并不会include @Configuration
的配置。
参考:https://stackoverflow.com/questions/39865596/difference-between-using-mockmvc-with-springboottest-and-using-webmvctest
@WebMvcTest
如同上述介绍的,会扫描它定义的Controller,并且引入Spring MVC相关的配置,也会自动注入MockMvc
instance,但并不会全局扫描并include所有的bean(比如不会扫描@Configuration
配置)。
SpringBootTest
则会启动整个Spring Application Context
,所以@Configuration
的配置自然也会被扫描以及注解,但并不会注入Spring MVC相关的比如MockMvc
。
1.2 若想include @Configuration,可以使用AutoConfigureMockMvc
在上述#1.1的BookControllerTest
,我们无法通过@WebMvcTest(BookController.class)
,来include @Configuration的配置,相应的,我们可以改用@AutoConfigureMockMvc
+@SpringBootTest
的方式:
@AutoConfigureMockMvc
@SpringBootTest
public class BookControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
BookService bookService;
@Autowired
private BookDomain bookDomain;
@Test
public void beanTest() {
Assertions.assertEquals(1001, bookDomain.getId());
Assertions.assertEquals("test bean", bookDomain.getName());
2. 测试get以及post APIs
以下示例测试了BookController中的两个APIs:
GET - /books
,返回BookDomain list。
POST - /books
,新增Book,接收RequestBody为BookDomain。
一些解释:
下述中的get,post方法,是位于org.springframework.test.web.servlet.request
包下,MockMvcRequestBuilders
类中,都是static方法。
关于BookService的行为,使用Mockito进行Mock。
除了能测试response status = 200外,也能测其它错误的Response。
print()则是打印结果。
测试MVC的写法很灵活,也可以测各种MediaType,比如application/json
或text/html
等等。
@WebMvcTest(BookController.class)
public class BookControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
BookService bookService;
@Test
public void listTest() throws Exception {
List<BookDomain> list = new ArrayList<>();
list.add(new BookDomain(1, "test"));
Mockito.when(bookService.list()).thenReturn(list);
MvcResult mvcResult = this.mockMvc.perform(get("/books").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(print())
.andReturn();
System.out.println(mvcResult.getResponse().getContentAsString());
Assertions.assertTrue(mvcResult.getResponse().getContentAsString().contains("test"));
@Test
public void saveTest() throws Exception {
Mockito.doNothing().when(bookService).save(Mockito.isA(BookDomain.class));
JsonMapper jsonMapper = new JsonMapper();
MvcResult mvcResult = this.mockMvc.perform(post("/books")
.contentType(MediaType.APPLICATION_JSON).content(jsonMapper.writeValueAsBytes(new BookDomain(1, "asdf"))))
.andExpect(status().isOk())
.andReturn();
Assertions.assertEquals("true", mvcResult.getResponse().getContentAsString());
3. 通过MockMvc
来测试我们自定义的Filter
假设我们有UserFilter,然后我们想要通过MockMvc
来测试自定义的Filter。
首先,自定义Filter有两种实现方式:
第一种方式:在Spring Boot启动类中加@ServletComponentScan
,并且在我们的UserFilter上加注解:@WebFilter(filterName = "userFilter", urlPatterns = "/*")
第二种方式,以Bean的方式注入。通过FilterRegistrationBean
新建Filter并返回,该方法标注为@Bean
。
根据#1介绍的,@WebMvcTest
注解只会扫描它定义的Controller类,并会忽略一切@Configuration
,所以我们要使用#1.2的方式,即:@AutoConfigureMockMvc
+@SpringBootTest
以下是代码示例。
首先新建UserFilter类:
public class UserFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("user filter");
filterChain.doFilter(servletRequest, servletResponse);
定义Configuration配置类:
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean userFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new UserFilter());
registration.addUrlPatterns("/*");
return registration;
沿用上述的BookControllerTest类:
@SpringBootTest
@AutoConfigureMockMvc
public class BookControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
BookService bookService;
@Test
public void listTest() throws Exception {
这样,在测试listTest的时候,会测试API为/books
(GET方法),最后会进入我们自定义的UserFilter。
Debug截图,说明能进入UserFilter: