使用MockMvc测试Spring mvc Controller

来源:互联网 发布:beta理财师软件 编辑:程序博客网 时间:2024/06/12 21:12

概述

    对模块进行集成测试时,希望能够通过输入URL对Controller进行测试,如果通过启动服务器,建立http client进行测试,这样会使得测试变得很麻烦,比如,启动速度慢,测试验证不方便,依赖网络环境等,这样会导致测试无法进行,为了可以对Controller进行测试,可以通过引入MockMVC进行解决。

简介

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

要求

   spring 集成测试中对mock 的集成很好,让开发员使用起来很方便,但是使用时必须注意以下三个条件:

  1. Junit必须在4.9版本以上
  2. spring版本必须在3.2以上
  3. 使用的框架必须是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 {@Autowiredprotected 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 */@Testpublic 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中。

更多详细的内容可以上网查阅相关的文章
感谢大家的支持!!!!






2 0