概述
对模块进行集成测试时,希望能够通过输入URL对Controller进行测试,如果通过启动服务器,建立http client进行测试,这样会使得测试变得很麻烦,比如,启动速度慢,测试验证不方便,依赖网络环境等,这样会导致测试无法进行,为了可以对Controller进行测试,可以通过引入MockMVC进行解决。
简介
MockMvc实现了对Http请求的模拟,能够直接使用网络的形式,转换到Controller的调用,这样可以使得测试速度快、不依赖网络环境,而且提供了一套验证的工具,这样可以使得请求的验证统一而且很方便。
要求
spring 集成测试中对mock 的集成很好,让开发员使用起来很方便,但是使用时必须注意以下三个条件:
-
Junit必须在4.9版本以上
-
spring版本必须在3.2以上
-
使用的框架必须是springMvc框架
现状
目前的测试流程图:
1.
直接使用httpClient
这方法各种麻烦
2.
使用Spring 提供的RestTemplate
错误不好跟踪,必须开着服务器
在
spring
开发中,可以使用Spring自带的MockMvc这个类进行Mock
测试
。
所谓的Mock测试,这里我举一个通俗易懂的例子,像servlet API中的HttpServletRequest对象是Tomcat容器生成的。我们无法手动的new出来,于是就有了所谓的Mock测试
运行配置
用到的注解
:
- RunWith(SpringJUnit4ClassRunner.class): 表示使用Spring Test组件进行单元测试;
- WebAppConfiguration: 使用这个Annotate会在跑单元测试的时候真实的启动一个web服务,然后开始调用Controller的Rest API,待单元测试跑完之后再将web服务停掉;
-
ContextConfiguration: 指定Bean的配置文件信息,可以有多种方式,这个例子使用的是文件路径形式,如果有多个配置文件,可以将括号中的信息配置为一个字符串数组来表示;
controller,component等都是使用注解,需要注解指定spring的配置文件,扫描相应的配置,将类初始化等。
- TransactionConfiguration(transactionManager=”transactionManager”,defaultRollback=true)配置事务的回滚,对数据库的增删改都会回滚,便于测试用例的循环利用
为什么要进行事务回滚:
- 测试过程对数据库的操作,会产生脏数据,影响我们数据的正确性
- 不方便循环测试,即假如这次我们将一个记录删除了,下次就无法再进行这个Junit测试了,因为该记录已经删除,将会报错。
- 如果不使用事务回滚,我们需要在代码中显式的对我们的增删改数据库操作进行恢复,将多很多和测试无关的代码
实际运用
父类
:
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
/**
* @author zl
* @version 创建时间:2017年3月14日 下午2:18:26
*
*/
//这个必须使用junit4.9以上才有
@RunWith(SpringJUnit4ClassRunner.class)
//单元测试的时候真实的开启一个web服务
@WebAppConfiguration
//配置事务的回滚,对数据库的增删改都会回滚,便于测试用例的循环利用
@TransactionConfiguration(transactionManager="transactionManager",defaultRollback=true)
@Transactional
@ContextConfiguration(locations = {"classpath:spring.xml","classpath:spring-hibernate.xml"})
public class AbstractContextControllerTests {
@Autowired
protected WebApplicationContext wac;
}
子类
:
package com.pengtu.gsj;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.hibernate.SessionFactory;
import org.junit.Before;
import org.junit.Test;
import org.owasp.esapi.ESAPI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.pengtu.gsj.controller.BannerController;
import com.pengtu.gsj.dao.UserDao;
import com.pengtu.gsj.entity.app.User;
import com.pengtu.gsj.service.UserService;
public class EsapiTest extends AbstractContextControllerTests{
private MockMvc mockMvc;
//该方法在每个方法执行之前都会执行一遍
@Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(new BannerController()).build();
}
/**
* perform:执行一个RequestBuilder请求,会自动执行SpringMVC的流程并映射到相应的控制器执行处理;
* get:声明发送一个get请求的方法。MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables):根据uri模板 和uri变量值得到一个GET请求方式的。另外提供了其他的请求的方法,如:post、put、delete等。
* param:添加request的参数,如上面发送请求的时候带上了了pcode = root的参数。假如使用需要发送json数据格式的时将不能使用这种 方式,可见后面被@ResponseBody注解参数的解决方法
* andExpect:添加ResultMatcher验证规则,验证控制器执行完成后结果是否正确(对返回的数据进行的判断);
* andDo:添加ResultHandler结果处理器,比如调试时打印结果到控制台(对返回的数据进行的判断);
* andReturn:最后返回相应的MvcResult;然后进行自定义验证/进行下一步的异步处理(对返回的数据进行的判断)
* @throws Exception
*/
@Test
public void getAllBanners() throws Exception{
String responseString = mockMvc.perform(get("/banner/hello") //请求的url,请求的方法是get
.contentType(MediaType.APPLICATION_JSON) //数据的格式
.param("id","123456789") //添加参数
).andExpect(status().isOk()) //返回的状态是200
.andDo(print()) //打印出请求和相应的内容
.andReturn().getResponse().getContentAsString(); //将相应的数据转换为字符串
System.out.println("--------返回的json = " + responseString);
}
}
对应controller的方法:
@RequestMapping("/hello")
@ResponseBody
public String index(String id) {
System.out.println("id:"+id);
return "Hello World";
}
执行测试类后的结果:
MockHttpServletRequest:
HTTP Method = GET
Request URI = /banner/hello
Parameters = {id=[123456789]}
Headers = {Content-Type=[application/json]}
Handler:
Type = com.pengtu.gsj.controller.BannerController
Method = public java.lang.String com.pengtu.gsj.controller.BannerController.index(java.lang.String)
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {Content-Type=[text/plain;charset=ISO-8859-1], Content-Length=[11]}
Content type = text/plain;charset=ISO-8859-1
Body = Hello World
Forwarded URL = null
Redirected URL = null
Cookies = []
--------返回的json = Hello World
-
perform
:
执行一个RequestBuilder请求
,会自动执行SpringMVC的流程并映射到相应的控制器执行处理;
-
get
:声明发送一个get请求的方法。MockHttpServletRequestBuilder get(String urlTemplate, Object… urlVariables):根据uri模板和uri变量值得到一个GET请求方式的。另外提供了其他的请求的方法,如:post、put、delete等。
-
param
:添加request的参数,如上面发送请求的时候带上了了pcode = root的参数。假如使用需要
发送json数据格式
的时将不能使用这种方式。
-
andExpect
:添加ResultMatcher验证规则,验证
控制器执行完成
后
结果是否正确(对返回的数据进行的判断);
-
andDo
:添加ResultHandler结果处理器,比如调试时打印结果到控制台(对返回的数据进行的判断);
-
andReturn
:最后返回相应的MvcResult;然后进行自定义验证/进行下一步的异步处理(对返回的数据进行的判断)
测试逻辑:
Mock出一个MockHttpServletRequestBuilder对象。用于模拟Http的get请求方式,
.param()方法可以给http请求携带参数,相当于 谢了这样一个url ——> http:localhost:8080/banner/hello?id=123456789
然后调用.andExport( status().isOk())方法看请求的状态响应码是否为200如果不是则抛异常,测试不通过
遇到的问题:
发送一个被@ResponseBody标识的参数,一直到400错误。 即无法发送一个json格式的数据到Controller层。
解决方法:
SoftInfo softInfo = new SoftInfo();
//。。。设置值
String requestJson = JSONObject.toJSONString(folderInfo);
String responseString = mockMvc.perform( post("/softs").contentType(MediaType.APPLICATION_JSON).content(requestJson)).andDo(print())
.andExpect(status().isOk()).andReturn().getResponse().getContentAsString();
注意上面contentType需要设置成MediaType.APPLICATION_JSON,即声明是发送“application/json”格式的数据。使用content方法,将转换的json数据放到request的body中。
更多详细的内容可以上网查阅相关的文章
感谢大家的支持!!!!
版权声明:本文为zhang289202241原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。