使用MockMvc测试Spring mvc Controller

概述

    对模块进行集成测试时,希望能够通过输入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中。

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






  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值