MockMvc基于RESTful风格的SpringMVC的测试框架,我们可以测试完整的Spring MVC流程,即从URL请求到控制器处理,再到视图渲染都可以测试。
一 MockMvcBuilder
MockMvcBuilder是用来构造MockMvc的构造器,其主要有两个实现:StandaloneMockMvcBuilder和DefaultMockMvcBuilder,分别对应两种测试方式,即独立安装和集成Web环境测试(此种方式并不会集成真正的web环境,而是通过相应的Mock API进行模拟测试,无须启动服务器)。对于我们来说直接使用静态工厂MockMvcBuilders创建即可。
1.集成Web环境方式
1.1xml引入测试
MockMvcBuilders.webAppContextSetup(WebApplicationContext context):指定WebApplicationContext,将会从该上下文获取相应的控制器并得到相应的MockMvc;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:config/IncotermsRestServiceTest-context.xml")
@WebAppConfiguration
public class IncotermsRestServiceTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); //构造MockMvc
}
...
}
注意:
(1)@WebAppConfiguration:测试环境使用,用来表示测试环境使用的ApplicationContext将是WebApplicationContext类型的;value指定web应用的根;
(2)通过@Autowired WebApplicationContext wac:注入web环境的ApplicationContext容器;
(3)然后通过MockMvcBuilders.webAppContextSetup(wac).build()创建一个MockMvc进行测试;
1.2SpringBoot测试
package com.yc.modules.test.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yc.AppRun;
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
import java.util.HashMap;
import java.util.Map;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
/**
* @create: 20190515
* @author: XueQing Wang
**/
@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = AppRun.class)
@WebAppConfiguration
@FixMethodOrder(MethodSorters.NAME_ASCENDING) // 顺序执行测试方法 方法名格式: test_02_Name
@Transactional() // 数据回滚
public class LoginTest {
MockMvc mvc;
@Autowired
WebApplicationContext webApplicationContext;
ObjectMapper mapper = new ObjectMapper();
// public static String token = "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTU1MjYyNzgxNCwiaWF0IjoxNTUyNjIwNjE0fQ.F-9z4RWfCgc_tvuHITFX5WAqGFx7i34nwyQK7NXiSYwHqhYm_Zsmgudc8t9bcDOt0VU5iEqCNF4zB_1ukFVa1A";
private String token = "";
@Before
public void setUp() throws Exception {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
HashMap
注意:
@FixMethodOrder注解为方法执行顺序
MethodSorters.JVM
Leaves the test methods in the order returned by the JVM.
Note that the order from the JVM may vary from run to run
(按照JVM得到的方法顺序,也就是代码中定义的方法顺序)
MethodSorters.DEFAULT(默认的顺序)
Sorts the test methods in a deterministic, but not predictable, order()
(以确定但不可预期的顺序执行)
MethodSorters.NAME_ASCENDING
Sorts the test methods by the method name, in lexicographic order,
with Method.toString() used as a tiebreaker
(按方法名字母顺序执行)
2.独立测试方式
MockMvcBuilders.standaloneSetup(Object... controllers):通过参数指定一组控制器,这样就不需要从上下文获取了;
public class PricingExportResultsRestServiceTest {
@InjectMocks
private PricingExportResultsRestService pricingExportResultsRestService;
@Mock
private ExportRateScheduleService exportRateScheduleService;
@Mock
private PricingUrlProvider pricingUrlProvider;
private MockMvc mockMvc;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(pricingExportResultsRestService).build(); //构造MockMvc
}
...
}
主要是两个步骤:
(1)首先自己创建相应的控制器,注入相应的依赖
(2)通过MockMvcBuilders.standaloneSetup模拟一个Mvc测试环境,通过build得到一个MockMvc
二 MockMvc
先看一个测试例子1:
@Test
public void createIncotermSuccess() throws Exception {
IncotermTo createdIncoterm = new IncotermTo();
createdIncoterm.setId(new IncotermId(UUID.fromString("6305ff33-295e-11e5-ae37-54ee7534021a")));
createdIncoterm.setCode("EXW");
createdIncoterm.setDescription("code exw");
createdIncoterm.setLocationQualifier(LocationQualifier.DEPARTURE);
when(inventoryService.create(any(IncotermTo.class))).thenReturn(createdIncoterm);
mockMvc.perform(post("/secured/resources/incoterms/create").accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON)
.content("{\"code\" : \"EXW\", \"description\" : \"code exw\", \"locationQualifier\" : \"DEPARTURE\"}".getBytes()))
//.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("id.value").exists())
.andExpect(jsonPath("id.value").value("6305ff33-295e-11e5-ae37-54ee7534021a"))
.andExpect(jsonPath("code").value("EXW"));
}
perform:执行一个RequestBuilder请求,会自动执行SpringMVC的流程并映射到相应的控制器执行处理;
andExpect:添加ResultMatcher验证规则,验证控制器执行完成后结果是否正确;
andDo:添加ResultHandler结果处理器,比如调试时打印结果到控制台;
andReturn:最后返回相应的MvcResult;然后进行自定义验证/进行下一步的异步处理;
看一个具体的例子2:
@Test
public void testView() throws Exception {
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/user/1"))
.andExpect(MockMvcResultMatchers.view().name("user/view"))
.andExpect(MockMvcResultMatchers.model().attributeExists("user"))
.andDo(MockMvcResultHandlers.print())
.andReturn();
Assert.assertNotNull(result.getModelAndView().getModel().get("user"));
}
整个过程:
1、mockMvc.perform执行一个请求;
2、MockMvcRequestBuilders.get("/user/1")构造一个请求
3、ResultActions.andExpect添加执行完成后的断言
4、ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情,比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息。
5、ResultActions.andReturn表示执行完成后返回相应的结果。
整个测试过程非常有规律:
1、准备测试环境
2、通过MockMvc执行请求
3.1、添加验证断言
3.2、添加结果处理器
3.3、得到MvcResult进行自定义断言/进行下一步的异步请求
4、卸载测试环境
三 RequestBuilder/MockMvcRequestBuilders
从名字可以看出,RequestBuilder用来构建请求的,其提供了一个方法buildRequest(ServletContext servletContext)用于构建MockHttpServletRequest;其主要有两个子类MockHttpServletRequestBuilder和MockMultipartHttpServletRequestBuilder(如文件上传使用),即用来Mock客户端请求需要的所有数据。
1.MockMvcRequestBuilders主要API
1.MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables):
根据uri模板和uri变量值得到一个GET请求方式的MockHttpServletRequestBuilder;如get(/user/{id}, 1L);
2.MockHttpServletRequestBuilder post(String urlTemplate, Object... urlVariables):
同get类似,但是是POST方法;
3.MockHttpServletRequestBuilder put(String urlTemplate, Object... urlVariables):
同get类似,但是是PUT方法;
4.MockHttpServletRequestBuilder delete(String urlTemplate, Object... urlVariables) :
同get类似,但是是DELETE方法;
5.MockHttpServletRequestBuilder options(String urlTemplate, Object... urlVariables):
同get类似,但是是OPTIONS方法;
6.MockHttpServletRequestBuilder request(HttpMethod httpMethod, String urlTemplate, Object... urlVariables):
提供自己的Http请求方法及uri模板和uri变量,如上API都是委托给这个API;
7.MockMultipartHttpServletRequestBuilder fileUpload(String urlTemplate, Object... urlVariables):
提供文件上传方式的请求,得到MockMultipartHttpServletRequestBuilder;
8.RequestBuilder asyncDispatch(final MvcResult mvcResult):
创建一个从启动异步处理的请求的MvcResult进行异步分派的RequestBuilder;
2.MockHttpServletRequestBuilder和MockMultipartHttpServletRequestBuilder API
(1)MockHttpServletRequestBuilder API
1.MockHttpServletRequestBuilder header(String name, Object... values)
MockHttpServletRequestBuilder headers(HttpHeaders httpHeaders):
添加头信息;
2.MockHttpServletRequestBuilder contentType(MediaType mediaType):
指定请求的contentType头信息;
3.MockHttpServletRequestBuilder accept(MediaType... mediaTypes)
MockHttpServletRequestBuilder accept(String... mediaTypes):
指定请求的Accept头信息;
4.MockHttpServletRequestBuilder content(byte[] content)
MockHttpServletRequestBuilder content(String content):
指定请求Body体内容;
5.MockHttpServletRequestBuilder cookie(Cookie... cookies):
指定请求的Cookie;
6.MockHttpServletRequestBuilder locale(Locale locale):
指定请求的Locale;
7.MockHttpServletRequestBuilder characterEncoding(String encoding):
指定请求字符编码;
8.MockHttpServletRequestBuilder requestAttr(String name, Object value) :
设置请求属性数据;
9.MockHttpServletRequestBuilder sessionAttr(String name, Object value)
MockHttpServletRequestBuilder sessionAttrs(Map sessionAttributes):
设置请求session属性数据;
10.MockHttpServletRequestBuilder flashAttr(String name, Object value)
MockHttpServletRequestBuilder flashAttrs(Map flashAttributes):
指定请求的flash信息,比如重定向后的属性信息;
11.MockHttpServletRequestBuilder session(MockHttpSession session) :
指定请求的Session;
12.MockHttpServletRequestBuilder principal(Principal principal) :
指定请求的Principal;
13.MockHttpServletRequestBuilder contextPath(String contextPath) :
指定请求的上下文路径,必须以“/”开头,且不能以“/”结尾;
14.MockHttpServletRequestBuilder pathInfo(String pathInfo) :
请求的路径信息,必须以“/”开头;
15.MockHttpServletRequestBuilder secure(boolean secure):
请求是否使用安全通道;
16.MockHttpServletRequestBuilder with(RequestPostProcessor postProcessor):
请求的后处理器,用于自定义一些请求处理的扩展点;
(2)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
1.ResultMatcher用来匹配执行完请求后的结果验证,其就一个match(MvcResult result)断言方法,如果匹配失败将抛出相应的异常;spring mvc测试框架提供了很多***ResultMatchers来满足测试需求。注意这些***ResultMatchers并不是ResultMatcher的子类,而是返回ResultMatcher实例的。Spring mvc测试框架为了测试方便提供了MockMvcResultMatchers静态工厂方法方便操作;
2.具体的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 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);
六 一些常用的测试
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