美文网首页
【测试相关】通过@WebMvcTest测试Spring Boot

【测试相关】通过@WebMvcTest测试Spring Boot

作者: 伊丽莎白2015 | 来源:发表于2022-10-05 15:55 被阅读0次

本文使用的是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,可以在 页面 中找到。
  • 另外,如果想要再额外include某些配置,可以使用@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/jsontext/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: image.png

【参考】

相关文章

网友评论

      本文标题:【测试相关】通过@WebMvcTest测试Spring Boot

      本文链接:https://www.haomeiwen.com/subject/fwhuartx.html