引入
由于MockMVC是Spring框架自带的测试组件,因此只要在项目中引入spring-boot-starter-test这个测试套件就可以使用Spring-test库中的MockMVC了。 例如Maven项目的pom.xml中添加如下的依赖
代码语言:javascript复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
以下将介绍如何使用MockMVC Mockito JUnit5 JsonUnit进行测试
待测Controller接口
代码语言:javascript复制package com.testlink4j.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.testlink4j.domain.Keywords;
import com.testlink4j.service.KeywordsService;
/**
* Keywords RestController
*
* @author Antony
*
*/
@RestController
public class KeywordsRestController {
@Autowired
private KeywordsService keywordsService;
@RequestMapping(value = "/api/keywords", method = RequestMethod.GET)
public Keywords findKeywordById(@RequestParam(value = "id", required = true) Integer id) {
return keywordsService.findKeywordById(id);
}
@PostMapping(value = "/api/keywords")
public Integer createKeywords(@RequestBody Keywords keywords) {
return keywordsService.createKeywords(keywords);
}
}
KeywordsRestController 包含了两个Keywords相关的接口,提供查询和创建的功能。 接下来,将以查询接口为例,介绍如何对该接口进行单元测试。
代码语言:javascript复制package com.testlink4j.controller;
import com.testlink4j.service.KeywordsService;
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.alibaba.fastjson.JSON;
import com.testlink4j.domain.Keywords;
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 java.io.UnsupportedEncodingException;
public class KeywordsControllerTest {
protected MockMvc mockMvc;
@Mock
private KeywordsService keywordsServic;
@InjectMocks
KeywordsRestController keywordsRestController;
@BeforeEach()
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(keywordsRestController).build(); //初始化MockMvc对象
}
@Test
public void CreateKeywordsSuccessfullyTest() throws UnsupportedEncodingException, Exception {
Keywords keywords=Keywords.builder().id(666).keyword("tester").testproject_id(333).notes("tester").build();
Mockito.when(keywordsServic.findKeywordById(1)).thenReturn(keywords);
String responseString = mockMvc.perform(
get("/api/keywords?id=1") //请求的url,请求的方法是Post
.contentType(MediaType.APPLICATION_JSON) //数据的格式
).andExpect(status().isOk()) //返回的状态是200
.andDo(print()) //打印出请求和相应的内容
.andReturn().getResponse().getContentAsString(); //将相应的数据转换为字符串
assertThatJson(responseString).isEqualTo(keywords);
}
}
findKeywordById接口通过调用KeywordsService来实现具体的查询功能。首先,和普通的基于Mockito单元测试一样,通过@Mock注解来对这个Service进行mock,并通过@InjectMocks注解实现注入。
接下来,通过
代码语言:javascript复制mockMvc = MockMvcBuilders.standaloneSetup(keywordsRestController).build()
将keywordsRestController加载进Spring容器。 在测试执行阶段,通过对URI的访问,查询id=1的keyword。 从测试结果来看,发生了如下的一系列过程 1)Spring容器收到访问请求,并由DispatcherServlet 根据@RequestMapping将请求转发给对应的controller的接口。 2)接口收到请求,通过解析@RequestParam获取入参,并调用对应的方法执行(调用service的测试桩来返回mock结果) 3)返回接口调用结果,即HttpServletResponse 4)对response的状态进行断言(200),并打印请求和响应 5)对响应结果进行断言(json)
来看一下用例执行过程中,通过print()方法打印的请求和响应
代码语言:javascript复制MockHttpServletRequest:
HTTP Method = GET
Request URI = /api/keywords
Parameters = {id=[1]}
Headers = [Content-Type:"application/json"]
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = com.testlink4j.controller.KeywordsRestController
Method = com.testlink4j.controller.KeywordsRestController#findKeywordById(Integer)
//中间部分省略
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"application/json"]
Content type = application/json
Body = {"id":666,"keyword":"tester","testproject_id":333,"notes":"tester"}
Forwarded URL = null
Redirected URL = null
Cookies = []
另外,看一下整个容器的启动耗时
19:29:57.689 [main] INFO org.springframework.test.web.servlet.TestDispatcherServlet - Completed initialization in 6 ms
可以看到,由于整个测试过程中只将被测的controller注入到了Spring容器中,容器的启动过程是非常快速的。
与直接通过类和方法调用的单元测试方式相比,通过使用MockMvc,有如下的不同 1)通过URI进行接口调用,也就是额外测试了DispatcherServlet 和@RequestMapping
2) 对@RequestParam进行了测试(感兴趣的读者可以尝试调用接口时不提供id=1的入参)
3)对接口返回进行了断言
4)对接口返回对象的反序列化进行了断言
下一篇将介绍如何使用MockMvc进行集成测试,并分析MockMVC的具体组成和使用方式。