Spring整合MockMvc进行单元测试,首先添加Spring-Test maven依赖:
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>4.3.7.RELEASEversion>
dependency>
Java配置如下:
package com.bob.test.config;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.UnsupportedEncodingException;
import com.bob.config.mvc.model.User;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.context.ContextConfiguration;
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.MvcResult;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.WebApplicationContext;
import com.bob.config.mvc.MvcContextConfig;
import com.bob.config.mvc.exception.CustomizedException;
import com.bob.config.root.RootContextConfig;
import com.google.gson.Gson;
/**
* @since 2016年12月8日 下午4:45:26
* @author
*
*/
@WebAppConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
//ContextConfiguration可以指定Java配置类,或者是配置文件,这里是基于Java配置形式
@ContextConfiguration(classes = { RootContextConfig.class, MvcContextConfig.class })
public abstract class BaseControllerTest {
protected Gson gson;
private MockMvc mockMvc;
protected MockHttpSession session;
protected Object mappedController;
protected String userName;
protected String password;
protected boolean loginBefore;
@Autowired
protected WebApplicationContext webApplicationContext;
@Before()
public void setup() {
gson = new Gson();
// 初始化MockMvc对象
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
init();
}
/**
* @param urlTemplate
* @param urlVariables
* @return
*/
public String getRequest(String urlTemplate, Object... urlVariables) {
return doRequest(RequestMethod.GET, null, false, null, urlTemplate, urlVariables);
}
/**
* @param urlTemplate
* @param urlVariables
* @return
*/
public String getAsyncRequest(String urlTemplate, Object... urlVariables) {
return doRequest(RequestMethod.GET, null, true, null, urlTemplate, urlVariables);
}
/**
* @param contentType
* @param content
* @param urlTemplate
* @param urlVariables
* @return
*/
public String postRequest(MediaType contentType, String content, String urlTemplate, Object... urlVariables) {
return doRequest(RequestMethod.POST, contentType, false, content, urlTemplate, urlVariables);
}
/**
* @param contentType
* @param content
* @param urlTemplate
* @param urlVariables
* @return
*/
public String postAsyncRequest(MediaType contentType, String content, String urlTemplate, Object... urlVariables) {
return doRequest(RequestMethod.POST, contentType, true, content, urlTemplate, urlVariables);
}
/**
* @param content
* @param urlTemplate
* @param urlVariables
* @return
*/
public String postRequest(String content, String urlTemplate, Object... urlVariables) {
return doRequest(RequestMethod.POST, MediaType.APPLICATION_JSON, false, content, urlTemplate, urlVariables);
}
/**
* @param content
* @param urlTemplate
* @param urlVariables
* @return
*/
public String postAsyncRequest(String content, String urlTemplate, Object... urlVariables) {
return doRequest(RequestMethod.POST, MediaType.APPLICATION_JSON, true, content, urlTemplate, urlVariables);
}
/**
* @param contentType
* @param content
* @param urlTemplate
* @param urlVariables
* @return
*/
public String putRequest(MediaType contentType, String content, String urlTemplate, Object... urlVariables) {
return doRequest(RequestMethod.PUT, contentType, false, content, urlTemplate, urlVariables);
}
/**
* @param contentType
* @param content
* @param urlTemplate
* @param urlVariables
* @return
*/
public String putAsyncRequest(MediaType contentType, String content, String urlTemplate, Object... urlVariables) {
return doRequest(RequestMethod.PUT, contentType, true, content, urlTemplate, urlVariables);
}
/**
* @param content
* @param urlTemplate
* @param urlVariables
* @return
*/
public String putRequest(String content, String urlTemplate, Object... urlVariables) {
return doRequest(RequestMethod.PUT, MediaType.APPLICATION_JSON, false, content, urlTemplate, urlVariables);
}
/**
* @param content
* @param urlTemplate
* @param urlVariables
* @return
*/
public String putAsyncRequest(String content, String urlTemplate, Object... urlVariables) {
return doRequest(RequestMethod.PUT, MediaType.APPLICATION_JSON, true, content, urlTemplate, urlVariables);
}
/**
* @param urlTemplate
* @param urlVariables
* @return
*/
public String deleteRequest(String urlTemplate, Object... urlVariables) {
return doRequest(RequestMethod.DELETE, null, false, null, urlTemplate, urlVariables);
}
/**
* @param urlTemplate
* @param urlVariables
* @return
*/
public String deleteAsyncRequest(String urlTemplate, Object... urlVariables) {
return doRequest(RequestMethod.DELETE, null, true, null, urlTemplate, urlVariables);
}
/**
* @param filePath
* @return
*/
public String readJsonFile(String filePath) {
BufferedReader bufferReader = null;
try {
File file = new File(filePath);
long length = file.length();
if (length > 5 * 1024 * 1024) {
throw new IllegalArgumentException("试图读取的文件大小超过5M");
}
return IOUtils.toString(new FileInputStream(file), "UTF-8");
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(filePath, e);
} finally {
try {
if (null != bufferReader) {
bufferReader.close();
}
} catch (Exception e) {
}
}
}
/**
*
* @param method
* @param contentType
* @param content
* @param urlTemplate
* @param urlVariables
* @return
*/
protected String doRequest(RequestMethod method, MediaType contentType, boolean async, String content, String urlTemplate, Object... urlVariables) {
checkMappedController(urlTemplate);
MockHttpServletRequestBuilder builder;
switch (method) {
case GET:
builder = MockMvcRequestBuilders.get(urlTemplate, urlVariables);
break;
case POST:
builder = MockMvcRequestBuilders.post(urlTemplate, urlVariables);
break;
case PUT:
builder = MockMvcRequestBuilders.put(urlTemplate, urlVariables);
break;
case DELETE:
builder = MockMvcRequestBuilders.delete(urlTemplate, urlVariables);
break;
default:
throw new UnsupportedOperationException(method.name());
}
if (loginBefore && login(userName, password)) {
builder.session(session);
}
if (content != null) {
builder.contentType(contentType).content(content);
}
try {
MvcResult mvcResult = mockMvc.perform(builder).andReturn();
return async ? mvcResult.getAsyncResult().toString() : mvcResult.getResponse().getContentAsString();
} catch (Exception e) {
return hanldeRequestException(e, method, contentType, content, urlTemplate, urlVariables);
}
}
/**
* 上传文件
*
* @param file
* 文件域名称
* @param path
* 路由
* @param urlTemplate
* @param async
* 是否是异步请求
* @param urlVariables
* @return
*/
public String fileUpload(String file, String path, String urlTemplate, boolean async, Object... urlVariables) {
checkMappedController(urlTemplate);
MockMultipartHttpServletRequestBuilder builder = MockMvcRequestBuilders.fileUpload(urlTemplate, urlVariables);
if (loginBefore && login(userName, password)) {
builder.session(session);
}
try {
builder.file(file, FileUtils.readFileToByteArray(new File(path)));
MvcResult mvcResult = mockMvc.perform(builder).andReturn();
return async ? mvcResult.getAsyncResult().toString() : mvcResult.getResponse().getContentAsString();
} catch (Exception e) {
return hanldeRequestException(e, RequestMethod.POST, null, null, urlTemplate, urlVariables);
}
}
/**
* @param e
* @param method
* @param contentType
* @param content
* @param urlTemplate
* @param urlVariables
* @return
*/
protected String hanldeRequestException(Exception e, RequestMethod method, MediaType contentType, String content, String urlTemplate,
Object... urlVariables) {
return null;
}
/**
* 登录
*
* @param userName
* @param password
* @return
* @throws Exception
* @throws UnsupportedEncodingException
*/
private boolean login(String userName, String password) {
User user = new User(userName, password);
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post("/users/login");
builder.contentType(MediaType.APPLICATION_JSON).content(gson.toJson(user));
String result = null;
try {
MvcResult mvc = mockMvc.perform(builder).andReturn();
this.session = (MockHttpSession) mvc.getRequest().getSession();
result = mvc.getResponse().getContentAsString();
} catch (Exception e) {
throw new IllegalStateException(String.format("用户名[%s],密码[%s]登录异常,请重新登录。", userName, password));
}
Assert.state(Boolean.parseBoolean(result), String.format("用户名[%s],密码[%s]登录失败,请验证用户名密码。", userName, password));
this.loginBefore = false;
return true;
}
/**
* 校验请求的路由是否匹配指定的Controller,主要根据Controller上的@RequestMapping()来校验
*
* @param urlTemplate
*/
private void checkMappedController(String urlTemplate) {
Assert.notNull(mappedController, "必须指明当前ControllerTest映射到哪个Controller");
Class> controllerClass = AopUtils.isAopProxy(mappedController) ? AopUtils.getTargetClass(mappedController) : mappedController.getClass();
RequestMapping mapping = controllerClass.getAnnotation(RequestMapping.class);
Assert.notNull(mapping, "mappedController必须含有[RequestMapping]注解");
boolean mapped = false;
for (String value : mapping.value()) {
if (value.contains("{")) {
value = value.substring(0, value.indexOf("{") + 1); // 当@RequestMapping("/user/{id}")形式时,截取"/user"
}
if (urlTemplate.startsWith(value)) {
mapped = true;
break;
}
}
if (!mapped) {
throw new CustomizedException(String.format("请求路由[%s]不匹配Controller:[%s]", urlTemplate, mappedController.getClass().getName()));
}
}
/**
* 模板方法,由子类重写以决定是否在运行之前登录
*/
protected abstract void init();
}
使用方法:
package com.bob.test.controller;
import com.bob.config.mvc.model.CacheModel;
import com.bob.test.config.BaseControllerTest;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import com.bob.mvc.controller.RedisCacheController;
import com.google.gson.Gson;
/**
* @since 2016年12月8日 下午5:16:52
* @version $Id$
* @author JiangJibo
*
*/
public class StudentControllerTest extends BaseControllerTest {
@Autowired //需要指定当前ControllerTest测试的是哪个Controller
private RedisCacheController studentController;
/* (non-Javadoc)
* @see com.bob.test.config.BaseControllerTest#init()
*/
@Override
protected void init() {
super.mappedController = studentController;
super.userName = "lanboal";
super.password = "123456";
//若需要在运行Controler测试前登陆,则需要在BaseControllerTest的login方法内指定你登陆的请求路径,
//同时设置好登陆名及密码,这样在运行单元测试前会先执行登陆操作,然后将登录的SESSION传递给当前的测试用例,
//当前测试时就能获取到用户的登录信息。
super.loginBefore = true;
}
@Test
public void testFormatDate() {
String result = this.getRequest("/stus/date?date=1988-07-26", "");
System.out.println("\t" + result);
}
@Test
public void testListAll() throws Exception {
//如果请求的是异步的,要求Controler的相应方法返回的对象时DeferredResult,Callable,WebAsyncTask这三个。
String result = this.getAsyncRequest("/stus?mediaType=xml", "");
System.out.println("\t" + result);
}
@Test
public void testListByAge() throws Exception {
String result = this.getRequest("/stus/age/27", "");
System.out.println("\t" + result);
}
@Test
public void testCreate() {
CacheModel sutdent = new CacheModel();
sutdent.setId(10);
sutdent.setAge(30);
sutdent.setAdress("金华");
sutdent.setTelephone("1748568745");
this.postRequest(new Gson().toJson(sutdent), "/stus", "");
}
}