使用MockMvc测试Spring mvc Controller

  • Post author:
  • Post category:其他


概述


对模块进行集成测试时,希望能够通过输入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 {
	
	@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 版权协议,转载请附上原文出处链接和本声明。