欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

springboot 单元测试 (controller层) 方法 -- MockMvc

程序员文章站 2022-04-26 18:11:40
...

一、什么是MockMvc?

       先来看看mock单词本身的意思(只看形容词的部分释义):adj.模拟的;仿制的;虚假的;不诚实的。那么通过字面理解MockMvc就是模拟的MVC环境。

     MockMvc是一套用于测试WEB应用的框架工具,它可以模拟HTTP请求来完成spring mvc的流程测试。有了它,我们不用再通过客户端访问服务端的方式来进行测试了,直接通过MockMvc模拟客户端即可,这样整个测试只需要在服务端就可以完成了。

二、MockMvc使用

1. 引入测试类上的注解

@RunWith(SpringRunner.class)
@SpringBootTest

2. 类中定义两个成员变量

    private MockMvc mockMvc;
    @Autowired
    private WebApplicationContext webApplicationContext;

3. 定义一个before方法,在执行测试方法之前准备测试环境

   注意(此处的MockMvc 实例化是通过手工方式创建,如果想通过spring的bean注入方式的话,在类上加注解@AutoConfigureMockMvc,然后在上面的第2步中进行注入,即在成员变量mockMvc上加注解@Autowired

    @Before
    public void setUp() {
    	//此种方式可通过spring上下文来自动配置一个或多个controller
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
        
        //此种方式,手工指定想要的controller
        //mockMvc = MockMvcBuilders.standaloneSetup(new Controller1(), new Controller2()).build();
    }

4. 编写测试方法@Test

在看测试方式之前先了解几个方法的作用

mockMvc.perform 执行一个请求
MockMvcRequestBuilders.post(“/appProducer/getAppLatestVersion”) 构造一个请求,get请求就用.get方法

MockHttpServletRequestBuilder.header("user-agent", "MicroMessenger") 请求头设置

MockHttpServletRequestBuilder.param("appId", "1001")  传递参数, get请求也可以通过这种方式传值,也可在地址后加参数方式:MockMvcRequestBuilders.get(“/appProducer/getAppLatestVersion?appId=1001”)
contentType(MediaType.APPLICATION_JSON_UTF8) 代表发送端发送的数据格式是application/json;charset=UTF-8
accept(MediaType.APPLICATION_JSON_UTF8) 代表客户端希望接受的数据类型为application/json;charset=UTF-8
session(session) 注入一个session,这样拦截器才可以通过
ResultActions.andExpect 添加执行完成后的断言
ResultActions.andExpect(MockMvcResultMatchers.status().isOk())方法看请求的状态响应码是否为200如果不是则抛异常,测试不通过
ResultActions.andExpect(jsonPath("$.status", not("E")))//使用jsonPath解析JSON返回值,判断具体的内容, 此处不希望status返回E,如果返回E测试不通过
ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情,比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息

 

下面的测试方法介绍了如何对接口返回的json数据进行处理、判断。通过jsonPath,用“$.属性”可以获取json中的具体的属性值。

	@Test
	public void TestGetAppLatestVersion() throws Exception{
		RequestBuilder request = null;
		//构造请求
		request = post("/appProducer/getAppLatestVersion") 
				.param("appId", "1001"); 
		//执行请求
		mockMvc.perform(request) 
		        .andExpect(status().isOk())//返回HTTP状态为200
		        .andExpect(jsonPath("$.status", not("E")))//使用jsonPath解析JSON返回值,判断具体的内容, 此处不希望status返回E
                .andExpect(content().string(containsString("选择浏览器打开即可")))//返回值为字符串,字符串包含比较,也可以字符串相等等比较,content()表示返回的结果
		        .andDo(print());//打印结果
		        //.andReturn();//想要返回结果,使用此方法
		
	}

如果不想通过上面的方式进行结果判断,也可以通过下面传统的断言方式进行判断

		MvcResult result = mockMvc.perform(request) 
		        .andReturn();//想要返回结果,使用此方法
		
		//可以通过上述方式将结果返回,然后通过传统的断言的方式进行结果判断
		Assert.assertNotNull(result.getResponse().getContentAsString());

        //建议使用新断言assertThat
//JUnit 4.4 结合 Hamcrest 提供了一个全新的断言语法——assertThat。程序员可以只使用 assertThat ////个断言语句,结合 Hamcrest 提供的匹配符,就可以表达全部的测试思想,我们引入的版本是Junit4.12所以支持assertThat。格式assertThat( [value], [matcher statement] );

下面再看一个接口中重定向url的验证,通过ResultMatcher redirectedUrl(final String expectedUrl)进行验证,来确保重定向的url地址正确

	/**
	 * 用户通过iPhone扫描下载APP,则会重定向至苹果APP官网,验证重定向后的url是否正确
	 */
	@Test
	public void TestDownloadAppByIphone() throws Exception{
		RequestBuilder request = null;
		//构造请求
		request = get("/appProducer/downloadApp")
				.header("user-agent", "iPhone")//设置头
				.param("appId", "1001"); 
		//执行请求
		mockMvc.perform(request) 
				.andExpect(status().is3xxRedirection())//表示页面被重定向
				.andExpect(redirectedUrl("https://www.apple.com/cn/itunes/charts1/"))//验证处理完请求后重定向的url
		        .andDo(print());//打印结果
		
	}

三、MockMvc相关API

RequestBuilder/MockMvcRequestBuilders:

在上面的测试类中,我们用到了这么一个类MockMvcRequestBuilders用来构建请求的,此类有以下主要的API:

MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables):根据uri模板和uri变量值得到一个GET请求方式的MockHttpServletRequestBuilder;如get(/user/{id}, 1L);
MockHttpServletRequestBuilder post(String urlTemplate, Object... urlVariables):同get类似,但是是POST方法;
MockHttpServletRequestBuilder put(String urlTemplate, Object... urlVariables):同get类似,但是是PUT方法;
MockHttpServletRequestBuilder delete(String urlTemplate, Object... urlVariables) :同get类似,但是是DELETE方法;
MockHttpServletRequestBuilder options(String urlTemplate, Object... urlVariables):同get类似,但是是OPTIONS方法;
MockHttpServletRequestBuilder request(HttpMethod httpMethod, String urlTemplate, Object... urlVariables): 提供自己的Http请求方法及uri模板和uri变量,如上API都是委托给这个API;
MockMultipartHttpServletRequestBuilder fileUpload(String urlTemplate, Object... urlVariables):提供文件上传方式的请求,得到MockMultipartHttpServletRequestBuilder;
RequestBuilder asyncDispatch(final MvcResult mvcResult):创建一个从启动异步处理的请求的MvcResult进行异步分派的RequestBuilder;
 

MockMvcRequestBuilders通过方法得到两类Builder,一个是MockHttpServletRequestBuilder ,一个是MockMultipartHttpServletRequestBuilder (上传文件)

MockHttpServletRequestBuilder:
MockHttpServletRequestBuilder 主要有一下API:

MockHttpServletRequestBuilder header(String name, Object... values)/MockHttpServletRequestBuilder headers(HttpHeaders httpHeaders):添加头信息;
MockHttpServletRequestBuilder contentType(MediaType mediaType):指定请求的contentType头信息;
MockHttpServletRequestBuilder accept(MediaType... mediaTypes)/MockHttpServletRequestBuilder accept(String... mediaTypes):指定请求的Accept头信息;
MockHttpServletRequestBuilder content(byte[] content)/MockHttpServletRequestBuilder content(String content):指定请求Body体内容;
MockHttpServletRequestBuilder param(String name,String... values):请求传入参数
MockHttpServletRequestBuilder cookie(Cookie... cookies):指定请求的Cookie;
MockHttpServletRequestBuilder locale(Locale locale):指定请求的Locale;
MockHttpServletRequestBuilder characterEncoding(String encoding):指定请求字符编码;
MockHttpServletRequestBuilder requestAttr(String name, Object value) :设置请求属性数据;
MockHttpServletRequestBuilder sessionAttr(String name, Object value)/MockHttpServletRequestBuilder sessionAttrs(Map<string, object=""> sessionAttributes):设置请求session属性数据;
MockHttpServletRequestBuilder flashAttr(String name, Object value)/MockHttpServletRequestBuilder flashAttrs(Map<string, object=""> flashAttributes):指定请求的flash信息,比如重定向后的属性信息;
MockHttpServletRequestBuilder session(MockHttpSession session) :指定请求的Session;
MockHttpServletRequestBuilder principal(Principal principal) :指定请求的Principal;
MockHttpServletRequestBuilder contextPath(String contextPath) :指定请求的上下文路径,必须以“/”开头,且不能以“/”结尾;
MockHttpServletRequestBuilder pathInfo(String pathInfo) :请求的路径信息,必须以“/”开头;
MockHttpServletRequestBuilder secure(boolean secure):请求是否使用安全通道;
MockHttpServletRequestBuilder with(RequestPostProcessor postProcessor):请求的后处理器,用于自定义一些请求处理的扩展点;

MockMultipartHttpServletRequestBuilder:
MockMultipartHttpServletRequestBuilder继承自MockHttpServletRequestBuilder,又提供了如下API:

MockMultipartHttpServletRequestBuilder file(String name, byte[] content)/MockMultipartHttpServletRequestBuilder file(MockMultipartFile file):指定要上传的文件;

ResultActions:
调用MockMvc.perform(RequestBuilder requestBuilder)后将得到ResultActions,通过ResultActions完成如下三件事:

ResultActions andExpect(ResultMatcher matcher) :添加验证断言来判断执行请求后的结果是否是预期的;
ResultActions andDo(ResultHandler handler) :添加结果处理器,用于对验证成功后执行的动作,如输出下请求/结果信息用于调试;
MvcResult andReturn() :返回验证成功后的MvcResult;用于自定义验证/下一步的异步处理;

ResultMatcher/MockMvcResultMatchers:
ResultMatcher用来匹配执行完请求后的结果验证,其就一个match(MvcResult result)断言方法,如果匹配失败将抛出相应的异常;此类案例中并为使用,请自行查看。具体提供以下API:

HandlerResultMatchers handler():请求的Handler验证器,比如验证处理器类型/方法名;此处的Handler其实就是处理请求的控制器;
RequestResultMatchers request():得到RequestResultMatchers验证器;
ModelResultMatchers model():得到模型验证器;
ViewResultMatchers view():得到视图验证器;
FlashAttributeResultMatchers flash():得到Flash属性验证;
StatusResultMatchers status():得到响应状态验证器;
HeaderResultMatchers header():得到响应Header验证器;
CookieResultMatchers cookie():得到响应Cookie验证器;
ContentResultMatchers content():得到响应内容验证器;
JsonPathResultMatchers jsonPath(String expression, Object ... args)/ResultMatcher jsonPath(String expression, Matcher matcher):得到Json表达式验证器;
XpathResultMatchers xpath(String expression, Object... args)/XpathResultMatchers xpath(String expression, Map<string, string=""> namespaces, Object... args):得到Xpath表达式验证器;
ResultMatcher forwardedUrl(final String expectedUrl):验证处理完请求后转发的url(绝对匹配);
ResultMatcher forwardedUrlPattern(final String urlPattern):验证处理完请求后转发的url(Ant风格模式匹配,@since spring4);
ResultMatcher redirectedUrl(final String expectedUrl):验证处理完请求后重定向的url(绝对匹配);
ResultMatcher redirectedUrlPattern(final String expectedUrl):验证处理完请求后重定向的url(Ant风格模式匹配,@since spring4);

四、附完整测试类

package js;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
/**
 * 测试用例,controller层,可测试所有controller
 * @description
 * @author GZY
 * 2019年3月18日 上午11:07:29
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class JsAppManageApplicationTests {

    private MockMvc mockMvc;
    @Autowired
    private WebApplicationContext webApplicationContext;
    
    @Before
    public void setUp() {
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }
    

    /**
     * 获取最新app信息
     */
	@Test
	public void TestGetAppLatestVersion() throws Exception{
		RequestBuilder request = null;
		//构造请求
		request = post("/appProducer/getAppLatestVersion") 
				.param("appId", "1001"); 
		//执行请求
		mockMvc.perform(request) 
		        .andExpect(status().isOk())//返回HTTP状态为200
		        .andExpect(jsonPath("$.status", not("E")))//使用jsonPath解析JSON返回值,判断具体的内容, 此处不希望status返回E
		        .andDo(print());//打印结果
		        //.andReturn();//想要返回结果,使用此方法
		
	}
	
	/**
	 * 用户通过微信方式扫描下载APP,则会提示使用浏览器打开地址
	 */
	@Test
	public void TestDownloadAppByMicroMessenger() throws Exception{
		RequestBuilder request = null;
		//构造请求
		request = get("/appProducer/downloadApp")
				.header("user-agent", "MicroMessenger")
				.param("appId", "1001"); 
		//执行请求
		mockMvc.perform(request) 
		        .andExpect(status().isOk())//返回HTTP状态为200
		        .andExpect(content().string(containsString("选择浏览器打开即可")))//返回结果中需包含的文字
		        .andDo(print());//打印结果
		
	}
	
	/**
	 * 用户通过Android扫描下载APP
	 */
	@Test
	public void TestDownloadAppByAndroid() throws Exception{
		RequestBuilder request = null;
		//构造请求
		request = get("/appProducer/downloadApp")
				.header("user-agent", "Android")
				.param("appId", "1001"); 
		//执行请求
		mockMvc.perform(request) 
		        .andExpect(status().isOk())//返回HTTP状态为200
		        .andDo(print());//打印结果
		
	}
	
	/**
	 * 用户通过iPhone扫描下载APP,则会重定向至评苹果APP官网
	 */
	@Test
	public void TestDownloadAppByIphone() throws Exception{
		RequestBuilder request = null;
		//构造请求
		request = get("/appProducer/downloadApp")
				.header("user-agent", "iPhone")
				.param("appId", "1001"); 
		//执行请求
		mockMvc.perform(request) 
				.andExpect(status().is3xxRedirection())//表示页面被重定向
				.andExpect(redirectedUrl("https://www.apple.com/cn/itunes/charts/"))//验证处理完请求后重定向的url
		        .andDo(print());//打印结果
		
	}
	
	/**
	 * 用户通过iPad扫描下载APP,则会重定向至评苹果APP官网
	 */
	@Test
	public void TestDownloadAppByIPad() throws Exception{
		RequestBuilder request = null;
		//构造请求
		request = get("/appProducer/downloadApp")
				.header("user-agent", "iPad")
				.param("appId", "1001"); 
		//执行请求
		mockMvc.perform(request) 
		        .andExpect(status().is3xxRedirection())//表示页面被重定向
		        .andExpect(redirectedUrl("https://www.apple.com/cn/itunes/charts/"))//验证处理完请求后重定向的url
		        .andDo(print());//打印结果
		
	}
	
	/**
	 * 用户通过其他方式扫描下载APP,则会提示仅支持的下载方式
	 */
	@Test
	public void TestDownloadAppByOther() throws Exception{
		RequestBuilder request = null;
		//构造请求
		request = get("/appProducer/downloadApp")
				.header("user-agent", "other")
				.param("appId", "1001"); 
		//执行请求
		mockMvc.perform(request) 
		        .andExpect(status().isOk())//返回HTTP状态为200
		        .andExpect(content().string(containsString("<h1>出现该页面可能是以下原因</h1>")))
		        .andDo(print());//打印结果
		
	}
	
	/**
	 * APP升级
	 */
	@Test
	public void TestUpgradeApp() throws Exception{
		RequestBuilder request = null;
		//构造请求
		request = get("/appProducer/upgradeApp")
				.param("appId", "1001"); 
		//执行请求
		mockMvc.perform(request) 
		        .andExpect(status().isOk())//返回HTTP状态为200
		        .andDo(print());//打印结果
		
	}
}

 

五、AssertThat

参考文章:https://www.cnblogs.com/wangcp-2014/p/4967055.html