一、什么是MockMvc?
先来看看mock单词本身的意思(只看形容词的部分释义):adj.模拟的;仿制的;虚假的;不诚实的。那么通过字面理解MockMvc就是模拟的MVC环境。
MockMvc是一套用于测试WEB应用的框架工具,它可以模拟HTTP请求来完成spring mvc的流程测试。有了它,我们不用再通过客户端访问服务端的方式来进行测试了,直接通过MockMvc模拟客户端即可,这样整个测试只需要在服务端就可以完成了。
二、MockMvc使用
1. 引入测试类上的注解
@RunWith(SpringRunner.class)
@SpringBootTest
2. 类中定义两个成员变量
private MockMvc mockMvc;
@Autowired
private WebApplicationContext webApplicationContext;
3. 定义一个before方法,在执行测试方法之前准备测试环境
注意(此处的MockMvc 实例化是通过手工方式创建,如果想通过spring的bean注入方式的话,在类上加注解@AutoConfigureMockMvc,然后在上面的第2步中进行注入,即在成员变量mockMvc上加注解@Autowired)
@Before
public void setUp() {
//此种方式可通过spring上下文来自动配置一个或多个controller
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
//此种方式,手工指定想要的controller
//mockMvc = MockMvcBuilders.standaloneSetup(new Controller1(), new Controller2()).build();
}
4. 编写测试方法@Test
在看测试方式之前先了解几个方法的作用
mockMvc.perform 执行一个请求
MockMvcRequestBuilders.post(“/appProducer/getAppLatestVersion”) 构造一个请求,get请求就用.get方法
MockHttpServletRequestBuilder.header("user-agent", "MicroMessenger") 请求头设置
MockHttpServletRequestBuilder.param("appId", "1001") 传递参数, get请求也可以通过这种方式传值,也可在地址后加参数方式:MockMvcRequestBuilders.get(“/appProducer/getAppLatestVersion?appId=1001”)
contentType(MediaType.APPLICATION_JSON_UTF8) 代表发送端发送的数据格式是application/json;charset=UTF-8
accept(MediaType.APPLICATION_JSON_UTF8) 代表客户端希望接受的数据类型为application/json;charset=UTF-8
session(session) 注入一个session,这样拦截器才可以通过
ResultActions.andExpect 添加执行完成后的断言
ResultActions.andExpect(MockMvcResultMatchers.status().isOk())方法看请求的状态响应码是否为200如果不是则抛异常,测试不通过
ResultActions.andExpect(jsonPath("$.status", not("E")))//使用jsonPath解析JSON返回值,判断具体的内容, 此处不希望status返回E,如果返回E测试不通过
ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情,比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息
下面的测试方法介绍了如何对接口返回的json数据进行处理、判断。通过jsonPath,用“$.属性”可以获取json中的具体的属性值。
@Test
public void TestGetAppLatestVersion() throws Exception{
RequestBuilder request = null;
//构造请求
request = post("/appProducer/getAppLatestVersion")
.param("appId", "1001");
//执行请求
mockMvc.perform(request)
.andExpect(status().isOk())//返回HTTP状态为200
.andExpect(jsonPath("$.status", not("E")))//使用jsonPath解析JSON返回值,判断具体的内容, 此处不希望status返回E
.andExpect(content().string(containsString("选择浏览器打开即可")))//返回值为字符串,字符串包含比较,也可以字符串相等等比较,content()表示返回的结果
.andDo(print());//打印结果
//.andReturn();//想要返回结果,使用此方法
}
如果不想通过上面的方式进行结果判断,也可以通过下面传统的断言方式进行判断
MvcResult result = mockMvc.perform(request)
.andReturn();//想要返回结果,使用此方法
//可以通过上述方式将结果返回,然后通过传统的断言的方式进行结果判断
Assert.assertNotNull(result.getResponse().getContentAsString());
//建议使用新断言assertThat
//JUnit 4.4 结合 Hamcrest 提供了一个全新的断言语法——assertThat。程序员可以只使用 assertThat 个断言语句,结合 Hamcrest 提供的匹配符,就可以表达全部的测试思想,我们引入的版本是Junit4.12所以支持assertThat。格式assertThat( [value], [matcher statement] );
下面再看一个接口中重定向url的验证,通过ResultMatcher redirectedUrl(final String expectedUrl)进行验证,来确保重定向的url地址正确
/**
* 用户通过iPhone扫描下载APP,则会重定向至苹果APP官网,验证重定向后的url是否正确
*/
@Test
public void TestDownloadAppByIphone() throws Exception{
RequestBuilder request = null;
//构造请求
request = get("/appProducer/downloadApp")
.header("user-agent", "iPhone")//设置头
.param("appId", "1001");
//执行请求
mockMvc.perform(request)
.andExpect(status().is3xxRedirection())//表示页面被重定向
.andExpect(redirectedUrl("https://www.apple.com/cn/itunes/charts1/"))//验证处理完请求后重定向的url
.andDo(print());//打印结果
}
三、MockMvc相关API
RequestBuilder/MockMvcRequestBuilders:
在上面的测试类中,我们用到了这么一个类MockMvcRequestBuilders用来构建请求的,此类有以下主要的API:
MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables):根据uri模板和uri变量值得到一个GET请求方式的MockHttpServletRequestBuilder;如get(/user/{id}, 1L);
MockHttpServletRequestBuilder post(String urlTemplate, Object... urlVariables):同get类似,但是是POST方法;
MockHttpServletRequestBuilder put(String urlTemplate, Object... urlVariables):同get类似,但是是PUT方法;
MockHttpServletRequestBuilder delete(String urlTemplate, Object... urlVariables) :同get类似,但是是DELETE方法;
MockHttpServletRequestBuilder options(String urlTemplate, Object... urlVariables):同get类似,但是是OPTIONS方法;
MockHttpServletRequestBuilder request(HttpMethod httpMethod, String urlTemplate, Object... urlVariables): 提供自己的Http请求方法及uri模板和uri变量,如上API都是委托给这个API;
MockMultipartHttpServletRequestBuilder fileUpload(String urlTemplate, Object... urlVariables):提供文件上传方式的请求,得到MockMultipartHttpServletRequestBuilder;
RequestBuilder asyncDispatch(final MvcResult mvcResult):创建一个从启动异步处理的请求的MvcResult进行异步分派的RequestBuilder;
MockMvcRequestBuilders通过方法得到两类Builder,一个是MockHttpServletRequestBuilder ,一个是MockMultipartHttpServletRequestBuilder (上传文件)
MockHttpServletRequestBuilder:
MockHttpServletRequestBuilder 主要有一下API:
MockHttpServletRequestBuilder header(String name, Object... values)/MockHttpServletRequestBuilder headers(HttpHeaders httpHeaders):添加头信息;
MockHttpServletRequestBuilder contentType(MediaType mediaType):指定请求的contentType头信息;
MockHttpServletRequestBuilder accept(MediaType... mediaTypes)/MockHttpServletRequestBuilder accept(String... mediaTypes):指定请求的Accept头信息;
MockHttpServletRequestBuilder content(byte[] content)/MockHttpServletRequestBuilder content(String content):指定请求Body体内容;
MockHttpServletRequestBuilder param(String name,String... values):请求传入参数
MockHttpServletRequestBuilder cookie(Cookie... cookies):指定请求的Cookie;
MockHttpServletRequestBuilder locale(Locale locale):指定请求的Locale;
MockHttpServletRequestBuilder characterEncoding(String encoding):指定请求字符编码;
MockHttpServletRequestBuilder requestAttr(String name, Object value) :设置请求属性数据;
MockHttpServletRequestBuilder sessionAttr(String name, Object value)/MockHttpServletRequestBuilder sessionAttrs(Map<string, object=""> sessionAttributes):设置请求session属性数据;
MockHttpServletRequestBuilder flashAttr(String name, Object value)/MockHttpServletRequestBuilder flashAttrs(Map<string, object=""> flashAttributes):指定请求的flash信息,比如重定向后的属性信息;
MockHttpServletRequestBuilder session(MockHttpSession session) :指定请求的Session;
MockHttpServletRequestBuilder principal(Principal principal) :指定请求的Principal;
MockHttpServletRequestBuilder contextPath(String contextPath) :指定请求的上下文路径,必须以“/”开头,且不能以“/”结尾;
MockHttpServletRequestBuilder pathInfo(String pathInfo) :请求的路径信息,必须以“/”开头;
MockHttpServletRequestBuilder secure(boolean secure):请求是否使用安全通道;
MockHttpServletRequestBuilder with(RequestPostProcessor postProcessor):请求的后处理器,用于自定义一些请求处理的扩展点;
MockMultipartHttpServletRequestBuilder:
MockMultipartHttpServletRequestBuilder继承自MockHttpServletRequestBuilder,又提供了如下API:
MockMultipartHttpServletRequestBuilder file(String name, byte[] content)/MockMultipartHttpServletRequestBuilder file(MockMultipartFile file):指定要上传的文件;
ResultActions:
调用MockMvc.perform(RequestBuilder requestBuilder)后将得到ResultActions,通过ResultActions完成如下三件事:
ResultActions andExpect(ResultMatcher matcher) :添加验证断言来判断执行请求后的结果是否是预期的;
ResultActions andDo(ResultHandler handler) :添加结果处理器,用于对验证成功后执行的动作,如输出下请求/结果信息用于调试;
MvcResult andReturn() :返回验证成功后的MvcResult;用于自定义验证/下一步的异步处理;
ResultMatcher/MockMvcResultMatchers:
ResultMatcher用来匹配执行完请求后的结果验证,其就一个match(MvcResult result)断言方法,如果匹配失败将抛出相应的异常;此类案例中并为使用,请自行查看。具体提供以下API:
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)/ResultMatcher jsonPath(String expression, Matcher matcher):得到Json表达式验证器;
XpathResultMatchers xpath(String expression, Object... args)/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);
四、附完整测试类
package js;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
/**
* 测试用例,controller层,可测试所有controller
* @description
* @author GZY
* 2019年3月18日 上午11:07:29
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class JsAppManageApplicationTests {
private MockMvc mockMvc;
@Autowired
private WebApplicationContext webApplicationContext;
@Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
/**
* 获取最新app信息
*/
@Test
public void TestGetAppLatestVersion() throws Exception{
RequestBuilder request = null;
//构造请求
request = post("/appProducer/getAppLatestVersion")
.param("appId", "1001");
//执行请求
mockMvc.perform(request)
.andExpect(status().isOk())//返回HTTP状态为200
.andExpect(jsonPath("$.status", not("E")))//使用jsonPath解析JSON返回值,判断具体的内容, 此处不希望status返回E
.andDo(print());//打印结果
//.andReturn();//想要返回结果,使用此方法
}
/**
* 用户通过微信方式扫描下载APP,则会提示使用浏览器打开地址
*/
@Test
public void TestDownloadAppByMicroMessenger() throws Exception{
RequestBuilder request = null;
//构造请求
request = get("/appProducer/downloadApp")
.header("user-agent", "MicroMessenger")
.param("appId", "1001");
//执行请求
mockMvc.perform(request)
.andExpect(status().isOk())//返回HTTP状态为200
.andExpect(content().string(containsString("选择浏览器打开即可")))//返回结果中需包含的文字
.andDo(print());//打印结果
}
/**
* 用户通过Android扫描下载APP
*/
@Test
public void TestDownloadAppByAndroid() throws Exception{
RequestBuilder request = null;
//构造请求
request = get("/appProducer/downloadApp")
.header("user-agent", "Android")
.param("appId", "1001");
//执行请求
mockMvc.perform(request)
.andExpect(status().isOk())//返回HTTP状态为200
.andDo(print());//打印结果
}
/**
* 用户通过iPhone扫描下载APP,则会重定向至评苹果APP官网
*/
@Test
public void TestDownloadAppByIphone() throws Exception{
RequestBuilder request = null;
//构造请求
request = get("/appProducer/downloadApp")
.header("user-agent", "iPhone")
.param("appId", "1001");
//执行请求
mockMvc.perform(request)
.andExpect(status().is3xxRedirection())//表示页面被重定向
.andExpect(redirectedUrl("https://www.apple.com/cn/itunes/charts/"))//验证处理完请求后重定向的url
.andDo(print());//打印结果
}
/**
* 用户通过iPad扫描下载APP,则会重定向至评苹果APP官网
*/
@Test
public void TestDownloadAppByIPad() throws Exception{
RequestBuilder request = null;
//构造请求
request = get("/appProducer/downloadApp")
.header("user-agent", "iPad")
.param("appId", "1001");
//执行请求
mockMvc.perform(request)
.andExpect(status().is3xxRedirection())//表示页面被重定向
.andExpect(redirectedUrl("https://www.apple.com/cn/itunes/charts/"))//验证处理完请求后重定向的url
.andDo(print());//打印结果
}
/**
* 用户通过其他方式扫描下载APP,则会提示仅支持的下载方式
*/
@Test
public void TestDownloadAppByOther() throws Exception{
RequestBuilder request = null;
//构造请求
request = get("/appProducer/downloadApp")
.header("user-agent", "other")
.param("appId", "1001");
//执行请求
mockMvc.perform(request)
.andExpect(status().isOk())//返回HTTP状态为200
.andExpect(content().string(containsString("<h1>出现该页面可能是以下原因</h1>")))
.andDo(print());//打印结果
}
/**
* APP升级
*/
@Test
public void TestUpgradeApp() throws Exception{
RequestBuilder request = null;
//构造请求
request = get("/appProducer/upgradeApp")
.param("appId", "1001");
//执行请求
mockMvc.perform(request)
.andExpect(status().isOk())//返回HTTP状态为200
.andDo(print());//打印结果
}
}
五、AssertThat
参考文章:https://www.cnblogs.com/wangcp-2014/p/4967055.html