SpringBoot基础之MockMvc单元测试

简介: SpringBoot基础之MockMvc单元测试

SpringBoot创建的Maven项目中,会默认添加spring-boot-starter-test依赖。在《5分钟快速上手SpringBoot》中编写的单元测试使用了MockMvc。本篇文章就围绕MockMvc在SpringBoot中的使用进行讲解。


什么是Mock


在面向对象的程序设计中,模拟对象(英语:mock object)是以可控的方式模拟真实对象行为的假对象。在编程过程中,通常通过模拟一些输入数据,来验证程序是否达到预期结果。


为什么使用Mock对象


使用模拟对象,可以模拟复杂的、真实的对象行为。如果在单元测试中无法使用真实对象,可采用模拟对象进行替代。


在以下情况可以采用模拟对象来替代真实对象:


- 真实对象的行为是不确定的(例如,当前的时间或温度);

- 真实对象很难搭建起来;

- 真实对象的行为很难触发(例如,网络错误);

- 真实对象速度很慢(例如,一个完整的数据库,在测试之前可能需要初始化);

- 真实的对象是用户界面,或包括用户界面在内;

- 真实的对象使用了回调机制;

- 真实对象可能还不存在;

- 真实对象可能包含不能用作测试(而不是为实际工作)的信息和方法。


使用Mockito一般分三个步骤:1、模拟测试类所需的外部依赖;2、执行测试代码;3、判断执行结果是否达到预期;


MockMvc


MockMvc是由spring-test包提供,实现了对Http请求的模拟,能够直接使用网络的形式,转换到Controller的调用,使得测试速度快、不依赖网络环境。同时提供了一套验证的工具,结果的验证十分方便。


接口MockMvcBuilder,提供一个唯一的build方法,用来构造MockMvc。主要有两个实现:StandaloneMockMvcBuilder和DefaultMockMvcBuilder,分别对应两种测试方式,即独立安装和集成Web环境测试(并不会集成真正的web环境,而是通过相应的Mock API进行模拟测试,无须启动服务器)。MockMvcBuilders提供了对应的创建方法standaloneSetup方法和webAppContextSetup方法,在使用时直接调用即可。


SpringBoot中使用


第一步:jar包引入。创建SpringBoot项目中默认引入的spring-boot-starter-test间接引入了spring-test,因此无需再额外引入jar包。






<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-test</artifactId>    <scope>test</scope></dependency>

第二步:创建HelloWorldController类,并提供hello方法作为待测试的业务接口。









@RestControllerpublic class HelloWorldController {
  @RequestMapping  public String hello(String name){    return "Hello " + name + "!";  }}


第三步:编写测试类。实例化MockMvc有两种形式,一种是使用StandaloneMockMvcBuilder,另外一种是使用DefaultMockMvcBuilder。测试类及初始化MockMvc初始化:























//SpringBoot1.4版本之前用的是SpringJUnit4ClassRunner.class@RunWith(SpringRunner.class)//SpringBoot1.4版本之前用的是@SpringApplicationConfiguration(classes = Application.class)@SpringBootTest//测试环境使用,用来表示测试环境使用的ApplicationContext将是WebApplicationContext类型的@WebAppConfigurationpublic class HelloWorldTest {
  private MockMvc mockMvc;
  @Autowired  private WebApplicationContext webApplicationContext;
  @Before  public void setup() {    // 实例化方式一    mockMvc = MockMvcBuilders.standaloneSetup(new HelloWorldController()).build();    // 实例化方式二//    mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();  }

单元测试方法:
























@Testpublic void testHello() throws Exception {
  /*   * 1、mockMvc.perform执行一个请求。   * 2、MockMvcRequestBuilders.get("XXX")构造一个请求。   * 3、ResultActions.param添加请求传值   * 4、ResultActions.accept(MediaType.TEXT_HTML_VALUE))设置返回类型   * 5、ResultActions.andExpect添加执行完成后的断言。   * 6、ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情   *   比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息。   * 7、ResultActions.andReturn表示执行完成后返回相应的结果。   */  mockMvc.perform(MockMvcRequestBuilders      .get("/hello")      // 设置返回值类型为utf-8,否则默认为ISO-8859-1      .accept(MediaType.APPLICATION_JSON_UTF8_VALUE)      .param("name", "Tom"))      .andExpect(MockMvcResultMatchers.status().isOk())      .andExpect(MockMvcResultMatchers.content().string("Hello Tom!"))      .andDo(MockMvcResultHandlers.print());}

测试结果打印:














FlashMap:       Attributes = null
MockHttpServletResponse:           Status = 200    Error message = null          Headers = [Content-Type:"application/json;charset=UTF-8", Content-Length:"10"]     Content type = application/json;charset=UTF-8             Body = Hello Tom!    Forwarded URL = null   Redirected URL = null          Cookies = []2019-04-02 21:34:27.954  INFO 6937 --- [       Thread-2] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'


整个过程如下:

1、准备测试环境

2、通过MockMvc执行请求

3、添加验证断言

4、添加结果处理器

5、得到MvcResult进行自定义断言/进行下一步的异步请求

6、卸载测试环境


注意事项:如果使用DefaultMockMvcBuilder进行MockMvc实例化时需在SpringBoot启动类上添加组件扫描的package的指定,否则会出现404。如:


@ComponentScan(basePackages = "com.secbro2")


相关API


RequestBuilder提供了一个方法buildRequest(ServletContext servletContext)用于构建MockHttpServletRequest;其主要有两个子类MockHttpServletRequestBuilder和MockMultipartHttpServletRequestBuilder(如文件上传使用)。


MockMvcRequestBuilders提供get、post等多种方法用来实例化RequestBuilder。


ResultActions,MockMvc.perform(RequestBuilder requestBuilder)的返回值,提供三种能力:andExpect,添加断言判断结果是否达到预期;andDo,添加结果处理器,比如示例中的打印;andReturn,返回验证成功后的MvcResult,用于自定义验证/下一步的异步处理。


一些常用的测试


1.测试普通控制器







mockMvc.perform(get("/user/{id}", 1)) //执行请求              .andExpect(model().attributeExists("user")) //验证存储模型数据              .andExpect(view().name("user/view")) //验证viewName              .andExpect(forwardedUrl("/WEB-INF/jsp/user/view.jsp"))//验证视图渲染时forward到的jsp              .andExpect(status().isOk())//验证状态码              .andDo(print()); //输出MvcResult到控制台


2.得到MvcResult自定义验证




MvcResult result = mockMvc.perform(get("/user/{id}", 1))//执行请求          .andReturn(); //返回MvcResult  Assert.assertNotNull(result.getModelAndView().getModel().get("user")); //自定义断言


3.验证请求参数绑定到模型数据及Flash属性








mockMvc.perform(post("/user").param("name", "zhang")) //执行传递参数的POST请求(也可以post("/user?name=zhang"))              .andExpect(handler().handlerType(UserController.class)) //验证执行的控制器类型              .andExpect(handler().methodName("create")) //验证执行的控制器方法名              .andExpect(model().hasNoErrors()) //验证页面没有错误              .andExpect(flash().attributeExists("success")) //验证存在flash属性              .andExpect(view().name("redirect:/user")); //验证视图


4.文件上传





byte[] bytes = new byte[] {1, 2};  mockMvc.perform(fileUpload("/user/{id}/icon", 1L).file("icon", bytes)) //执行文件上传          .andExpect(model().attribute("icon", bytes)) //验证属性相等性          .andExpect(view().name("success")); //验证视图


5.JSON请求/响应验证
















String requestBody = "{\"id\":1, \"name\":\"zhang\"}";      mockMvc.perform(post("/user")              .contentType(MediaType.APPLICATION_JSON).content(requestBody)              .accept(MediaType.APPLICATION_JSON)) //执行请求              .andExpect(content().contentType(MediaType.APPLICATION_JSON)) //验证响应contentType              .andExpect(jsonPath("$.id").value(1)); //使用Json path验证JSON 请参考http://goessner.net/articles/JsonPath/            String errorBody = "{id:1, name:zhang}";      MvcResult result = mockMvc.perform(post("/user")              .contentType(MediaType.APPLICATION_JSON).content(errorBody)              .accept(MediaType.APPLICATION_JSON)) //执行请求              .andExpect(status().isBadRequest()) //400错误请求              .andReturn();            Assert.assertTrue(HttpMessageNotReadableException.class.isAssignableFrom(result.getResolvedException().getClass()));//错误的请求内容体


6.异步测试











//Callable  MvcResult result = mockMvc.perform(get("/user/async1?id=1&name=zhang")) //执行请求          .andExpect(request().asyncStarted())          .andExpect(request().asyncResult(CoreMatchers.instanceOf(User.class))) //默认会等10秒超时          .andReturn();    mockMvc.perform(asyncDispatch(result))          .andExpect(status().isOk())          .andExpect(content().contentType(MediaType.APPLICATION_JSON))          .andExpect(jsonPath("$.id").value(1));


7.全局配置










mockMvc = webAppContextSetup(wac)              .defaultRequest(get("/user/1").requestAttr("default", true)) //默认请求 如果其是Mergeable类型的,会自动合并的哦mockMvc.perform中的RequestBuilder              .alwaysDo(print())  //默认每次执行请求后都做的动作              .alwaysExpect(request().attribute("default", true)) //默认每次执行后进行验证的断言              .build();        mockMvc.perform(get("/user/1"))         .andExpect(model().attributeExists("user"));


部分内容参考:https://www.cnblogs.com/lyy-2016/p/6122144.html


目录
相关文章
|
2月前
|
Java 测试技术 开发者
必学!Spring Boot 单元测试、Mock 与 TestContainer 的高效使用技巧
【10月更文挑战第18天】 在现代软件开发中,单元测试是保证代码质量的重要手段。Spring Boot提供了强大的测试支持,使得编写和运行测试变得更加简单和高效。本文将深入探讨Spring Boot的单元测试、Mock技术以及TestContainer的高效使用技巧,帮助开发者提升测试效率和代码质量。
309 2
|
2月前
|
XML Java 测试技术
【SpringBoot系列】初识Springboot并搭建测试环境
【SpringBoot系列】初识Springboot并搭建测试环境
91 0
|
20天前
|
安全 Java 测试技术
springboot之SpringBoot单元测试
本文介绍了Spring和Spring Boot项目的单元测试方法,包括使用`@RunWith(SpringJUnit4ClassRunner.class)`、`@WebAppConfiguration`等注解配置测试环境,利用`MockMvc`进行HTTP请求模拟测试,以及如何结合Spring Security进行安全相关的单元测试。Spring Boot中则推荐使用`@SpringBootTest`注解简化测试配置。
|
28天前
|
Java 测试技术 API
详解Swagger:Spring Boot中的API文档生成与测试工具
详解Swagger:Spring Boot中的API文档生成与测试工具
38 4
|
2月前
|
安全 Java 数据库
shiro学习一:了解shiro,学习执行shiro的流程。使用springboot的测试模块学习shiro单应用(demo 6个)
这篇文章是关于Apache Shiro权限管理框架的详细学习指南,涵盖了Shiro的基本概念、认证与授权流程,并通过Spring Boot测试模块演示了Shiro在单应用环境下的使用,包括与IniRealm、JdbcRealm的集成以及自定义Realm的实现。
50 3
shiro学习一:了解shiro,学习执行shiro的流程。使用springboot的测试模块学习shiro单应用(demo 6个)
|
29天前
|
Java 测试技术 数据库连接
使用Spring Boot编写测试用例:实践与最佳实践
使用Spring Boot编写测试用例:实践与最佳实践
66 0
|
2月前
|
监控 Java Maven
springboot学习二:springboot 初创建 web 项目、修改banner、热部署插件、切换运行环境、springboot参数配置,打包项目并测试成功
这篇文章介绍了如何快速创建Spring Boot项目,包括项目的初始化、结构、打包部署、修改启动Banner、热部署、环境切换和参数配置等基础操作。
164 0
|
3月前
|
JavaScript 前端开发 Java
Spring Boot+cucumber+契约测试
Spring Boot+cucumber+契约测试
33 0
Spring Boot+cucumber+契约测试
|
4月前
|
Java 测试技术
SpringBoot单元测试快速写法问题之区分链路环节是否应该被Mock如何解决
SpringBoot单元测试快速写法问题之区分链路环节是否应该被Mock如何解决
|
4月前
|
Java 测试技术 数据库
SpringBoot单元测试快速写法问题之不想在PandoraBoot工程中Mock Dao层如何解决
SpringBoot单元测试快速写法问题之不想在PandoraBoot工程中Mock Dao层如何解决