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

一、什么是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

 


好的,以下是一个简单的 Spring Boot 单元测试 controller 的示例代码: 假设我们有一个 UserController 类,其中有一个方法 getUserById,用于根据用户 ID 查询用户信息。我们可以编写一个 UserControllerTest 类,来测试这个方法。 UserController 类代码: ```java @RestController @RequestMapping("/users") public class UserController { @Autowired private UserService userService; @GetMapping("/{id}") public User getUserById(@PathVariable Long id) { return userService.getUserById(id); } } ``` UserControllerTest 类代码: ```java @RunWith(SpringRunner.class) @WebMvcTest(UserController.class) public class UserControllerTest { @Autowired private MockMvc mockMvc; @MockBean private UserService userService; @Test public void testGetUserById() throws Exception { // 准备测试数据 User user = new User(); user.setId(1L); user.setUsername("test"); user.setPassword("test123"); user.setAge(18); // 设置 userService 的行为 when(userService.getUserById(1L)).thenReturn(user); // 发送 GET 请求 mockMvc.perform(get("/users/1")) // 验证返回状态码 .andExpect(status().isOk()) // 验证返回数据 .andExpect(jsonPath("$.id").value(user.getId())) .andExpect(jsonPath("$.username").value(user.getUsername())) .andExpect(jsonPath("$.password").value(user.getPassword())) .andExpect(jsonPath("$.age").value(user.getAge())); } } ``` 该测试类使用了 @RunWith(SpringRunner.class) 注解来指定使用 SpringRunner 来运行测试,并使用了 @WebMvcTest(UserController.class) 注解来指定需要测试的 controller 类。 在测试方法中,我们首先使用 Mockito 的 @MockBean 注解来创建一个 UserService 的 mock 对象,然后通过 when(userService.getUserById(1L)).thenReturn(user) 来设置 mock 对象的行为。 接下来,我们使用 MockMvc 发送 GET 请求,并通过 andExpect() 方法来验证返回状态码和返回数据。在这个例子中,我们使用了 jsonPath() 方法来验证返回的 JSON 数据。 需要注意的是,该测试类并没有启动整个 Spring Boot 应用程序,而是只启动了一个 mock servlet 容器,这样测试效率会更高。 希望这个示例能够帮到你。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值