在之前的 Spring学习之旅(八)--SpringMVC请求参数 我们是通过在控制台输出来验证参数是否正确,但是这样做实在是太耗时间了,我们今天来学习下 MockMvc,它可以让我们不需要启动项目就能调用接口并验证接口返回结果是否符合我们的预期。

为何使用MockMvc?

MockMvc 实现了对 Http 请求的模拟,能够直接使用网络的形式,实现 Controller 的调用,这样可以使得测试速度快、不依赖网络环境,而且提供了一套验证的工具,这样可以使得请求的验证统一而且很方便。

如何使用 MockMvc

MockMvcBuilder 是用来构造 MockMvc 的构造器,主要有两个实现:

  • StandaloneMockMvcBuilder
  • DefaultMockMvcBuilder

分别对应两种测试方式,即独立安装和集成 Web 环境测试(此种方式并不会集成真正的 web 环境,而是通过相应的 Mock API 进行模拟测试,无须启动服务器)。对于我们来说直接使用静态工厂 MockMvcBuilders 创建即可。

集成Web环境方式

MockMvcBuilders.webAppContextSetup(WebApplicationContext context) :指定 WebApplicationContext,将会从该上下文获取相应的控制器并得到相应的 MockMvc

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = RootConfig.class)
@WebAppConfiguration
public class RequestParameterControllerTest { @Autowired
private WebApplicationContext wac; private MockMvc mockMvc; @Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
}

独立测试方式

MockMvcBuilders.standaloneSetup(Object... controllers) :通过参数指定一组控制器,这样就不需要从上下文获取了;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = RootConfig.class)
@WebAppConfiguration
public class RequestParameterControllerTest { @Autowired
private WebApplicationContext wac; private MockMvc mockMvc; @Before
public void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(new RequestParameterController()).build();
}
}

实例

Controller:

@Controller
public class RequestParameterController { @RequestMapping("/toInt")
@ResponseBody
public int toInt(int value) {
return value;
}
}

单元测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = RootConfig.class)
@WebAppConfiguration
public class RequestParameterControllerTest { @Autowired
private WebApplicationContext wac; private MockMvc mockMvc; @Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
} @Test
public void toInt() throws Exception {
mockMvc.perform(
// 发送 GET 请求
MockMvcRequestBuilders.get("/toInteger?value=3"))
// 判断HTTP响应码
.andExpect(MockMvcResultMatchers.status().isOk())
// 判断返回内容是否是预期值
.andExpect(MockMvcResultMatchers.content().string("3"))
// 输出整个响应结果信息
.andDo(MockMvcResultHandlers.print());
} }

控制台输出:

MockHttpServletRequest:
HTTP Method = GET
Request URI = /toInteger
Parameters = {value=[3]}
Headers = {} Handler:
Type = com.marklogzhu.web.controller.RequestParameterController Async:
Async started = false
Async result = null Resolved Exception:
Type = null ModelAndView:
View name = null
View = null
Model = null FlashMap:
Attributes = null MockHttpServletResponse:
Status = 200
Error message = null
Headers = {Content-Type=[text/plain;charset=ISO-8859-1], Content-Length=[1]}
Content type = text/plain;charset=ISO-8859-1
Body = 3
Forwarded URL = null
Redirected URL = null
Cookies = [] Process finished with exit code 0

常用对象

MockMvcRequestBuilders

MockMvcRequestBuilders: 用来构建请求。

方法 作用
MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables) 发送 GET 请求
MockHttpServletRequestBuilder post(String urlTemplate, Object... urlVariables) 发送 POST 请求
MockHttpServletRequestBuilder put(String urlTemplate, Object... urlVariables) 发送 PUT 请求
MockHttpServletRequestBuilder delete(String urlTemplate, Object... urlVariables) 发送 DELETE 请求
MockHttpServletRequestBuilder options(String urlTemplate, Object... urlVariables) 发送 OPTIONS 请求
MockHttpServletRequestBuilder request(HttpMethod httpMethod, String urlTemplate, Object... urlVariables) 提供自己的Http请求方法及uri模板和uri变量
MockMultipartHttpServletRequestBuilder fileUpload(String urlTemplate, Object... urlVariables) 发送文件上传请求
RequestBuilder asyncDispatch(final MvcResult mvcResult) 创建一个启动异步处理的请求的 MvcResult 进行异步分派的RequestBuilder

ResultActions

调用 MockMvc.perform(RequestBuilder requestBuilder) 后将得到 ResultActions,通过 ResultActions 可以完成如下三件事:

  • ResultActions andExpect(ResultMatcher matcher) :添加验证断言来判断执行请求后的结果是否是预期的;
  • ResultActions andDo(ResultHandler handler) :添加结果处理器,用于对验证成功后执行的动作,如输出下请求/结果信息用于调试;
  • MvcResult andReturn() :返回验证成功后的MvcResult;用于自定义验证/下一步的异步处理;

ResultMatcher

方法 作用
HandlerResultMatchers handler() 请求的 Handler 验证器,比如验证处理器类型/方法名;此处的 Handler 其实就是处理请求的控制器
RequestResultMatchers request() 得到 RequestResultMatchers 验证器
ModelResultMatchers model() 得到模型验证器
ViewResultMatchers view() 得到视图验证器
FlashAttributeResultMatchers flash() 得到 Flash 属性验证
StatusResultMatchers status() 得到响应状态验证器
HeaderResultMatchers header() 得到响应 Header 验证器
CookieResultMatchers cookie() 得到响应 Cookie 验证器
ContentResultMatchers content() 得到响应内容验证器
JsonPathResultMatchers jsonPath(String expression, Object ... args) 得到Json表达式验证器
ResultMatcher jsonPath(String expression, Matcher matcher) 得到Json表达式验证器
XpathResultMatchers xpath(String expression, Object... args) 得到Xpath表达式验证器
XpathResultMatchers xpath(String expression, Map<string, string=""> namespaces, Object... args) 得到Xpath表达式验证器
ResultMatcher forwardedUrl(final String expectedUrl) 验证处理完请求后转发的url(绝对匹配)
ResultMatcher forwardedUrlPattern(final String urlPattern) 验证处理完请求后转发的url(Ant风格模式匹配,@since spring4)
ResultMatcher redirectedUrl(final String expectedUrl) 验证处理完请求后重定向的url(绝对匹配)
ResultMatcher redirectedUrlPattern(final String expectedUrl) 验证处理完请求后重定向的url(Ant风格模式匹配,@since spring4)

实例

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()));//错误的请求内容体

文件上传

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")); //验证视图

自定义验证

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

Spring学习之旅(十)--MockMvc的更多相关文章

  1. Spring学习之旅(十五)--SpringBoot

    在使用 Spring 的过程中,有时候会出现一些 ClassNotFoundException 异常,这是因为 JAR 依赖之间的版本不匹配所导致的.而 Spring Boot 就能避免绝大多数依赖版 ...

  2. Spring学习之旅(十四)--缓存

    数据库的读写并发一直都是应用性能的瓶颈所在之一,针对改动频率很小的数据我们应该将他存放到缓存中,减少与数据库的交互. 启用对缓存的支持 Spring 对缓存的支持有两种方式: 注解驱动的缓存 XML ...

  3. Spring学习之旅(十二)--持久化框架

    对于本职工作来说 JDBC 就可以很好的完成,但是当我们对持久化的需求变得更复杂时,如: 延迟加载 预先抓取 级联 JDBC 就不能满足了,我们需要使用 ORM框架 来实现这些需求. Spring 对 ...

  4. Java框架spring 学习笔记(十八):事务管理(xml配置文件管理)

    在Java框架spring 学习笔记(十八):事务操作中,有一个问题: package cn.service; import cn.dao.OrderDao; public class OrderSe ...

  5. Spring学习之旅(八)Spring 基于AspectJ注解配置的AOP编程工作原理初探

    由小编的上篇博文可以一窥基于AspectJ注解配置的AOP编程实现. 本文一下未贴出的相关代码示例请关注小编的上篇博文<Spring学习之旅(七)基于XML配置与基于AspectJ注解配置的AO ...

  6. Spring学习之旅(六)--SpringMVC集成

    对大多数 Java 开发来说,基于 web 的应用程序是我们主要的关注点. Spring 也提供了对于 web 的支持,基于 MVC 模式的 Spring MVC 能够帮助我们灵活和松耦合的完成 we ...

  7. Java框架spring 学习笔记(十四):注解aop操作

    回见Java框架spring Boot学习笔记(十三):aop实例操作,这里介绍注解aop操作 首先编写一个切入点HelloWorld.java package com.example.spring; ...

  8. Spring学习之旅(十三)--使用NoSQL数据库

    除了关系型数据库之外,现在还有一种 NoSQL 数据库非常流行,而 Spring 自然也没有放过对它的支持. NoSQL 数据库有很多种,如: MongoDBGenericJackson2JsonRe ...

  9. Spring学习之旅(三)--装配Bean

    装配 Bean 的方式 在 XML 中进行显式配置 在 Java 中进行显式配置 隐式的 Bean 发现机制和自动装配 Spring 提供了以上三种方式进行 Bean 的配置,可以根据自己的需求选择一 ...

随机推荐

  1. join,列表和字典用for循环的删除,集合,深浅拷贝

    1.join() 将列表转换成字符串,并且每个字符之间用另一个字符连接起来,join后面必须是可迭代的对象(字符串,列表,元组,字典,集合),数字不能迭代 例如: s = ['a','b','c'] ...

  2. linux初学者-iptables篇

     linux初学者-iptables篇 iptables是防火墙的一种,是用来设置.维护和检查linux内核的IP过滤规则的,可以完成封包过滤.封包重定向和网络地址转换(NAT)等功能. iptabl ...

  3. 正则与sed,grep,awk三剑客

    系统登录顺序: /etc/profile /etc/profile.d/a.sh (a.sh自己建的) /root/.bash_profile /root/.bashrc /etc/bashrc /b ...

  4. PHP与ECMAScript_1_变量与常量

    PHP ECMAScript 变量命名规则 (相同点) 变量包含:字母.数字.下划线字符 变量只能以字母或下划线开头 变量不能以数字开头 变量名是区分大小写 变量包含:字母.数字.下划线字符 变量只能 ...

  5. 1.4.3 ID遍历爬虫(每天一更)

    # -*- coding: utf-8 -*- ''' Created on 2019年5月7日 @author: 薛卫卫 ''' import itertools import urllib.req ...

  6. [ PyQt入门教程 ] PyQt5信号与槽

    信号和槽是PyQt编程对象之间进行通信的机制.每个继承自QWideget的控件都支持信号与槽机制.信号发射时(发送请求),连接的槽函数就会自动执行(针对请求进行处理).本文主要讲述信号和槽最基本.最经 ...

  7. 表单与v-model

    基本用法 vue.js提供了v-model指令,用于在表单类元素上双向绑定数据,例如在输入框上使用,输入的内容会实时映射到绑定的数据上 example1:修改input框内容,message会实时渲染 ...

  8. 为什么阿里Java规约禁止使用Java内置线程池?

    IDEA导入阿里规约插件,当你这样写代码时,插件就会自动监测出来,并给你红线提醒. 告诉你手动创建线程池,效果会更好. 在探秘原因之前我们要先了解一下线程池 ThreadPoolExecutor 都有 ...

  9. 使用 OpenSSL 为 Nginx 创建自签名证书 并开启客户端身份验证

    本文章默认读者了解Openssl,CA,网站证书相关知识,直接实战!配置完成后,浏览器会显示"安全的HTTPS"连接.不会像其他文章那样,是红色警告的证书提示. 准备环境 笔者使用 ...

  10. 【译】尝试使用Nullable Reference Types

    随着.NET Core 3.0 Preview 7的发布,C#8.0已被认为是“功能完整”的.这意味着它们的最大亮点Nullable Reference Types,在行为方面也被锁定在.NET Co ...