一、背景介绍
在进行集成测试时,希望能够对Controller进行测试,如果通过启动服务器,建立 HTTP Client 进行测试,这样会使得测试变得很麻烦,比如,启动速度慢,测试验证不方便,依赖网络环境等,这样会导致测试无法进行,为了可以对 Controller 进行测试,可以通过引入 MockMVC 进行解决。
MockMvc 实现了对 HTTP 请求的模拟,能够直接使用网络的形式,转换到 Controller 的调用,这样可以使得测试速度快、不依赖网络环境,而且提供了一套验证的工具,这样可以使得请求的验证统一而且很方便。
二、MockMvc 使用
2.1 构建 MockMvc
通过 MockMvcBuilder 可以构建 MockMvc 对象,它主要有如下两个实现:
-
StandaloneMockMvcBuilder: 独立安装测试
private MockMvc mockMvc; @BeforeEach public void setUp() { mockMvc = MockMvcBuilders.standaloneSetup(new TestController()).build(); }
-
DefaultMockMvcBuilder:集成 Web 服务测试 ,建议使用这种方式
@Autowired private WebApplicationContext context; private MockMvc mockMvc; @BeforeEach public void setUp() { mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); }
2.2 构建请求
通过 MockMvcRequestBuilders 来构建请求,它有以下方法:
- get
- post
- put
- delete
- ......
2.3 执行请求
MockMvc 通过调用 perform(RequestBuilder requestBuilder) 方法发起请求,将会返回一个操作结果 ResultActions。
2.4 操作结果处理
对 ResultActions有以下三种处理:
-
ResultActions.andExpect:添加执行完成后的断言。添加 ResultMatcher 验证规则,验证控制器执行完成后结果是否正确;
-
ResultActions.andDo:添加一个结果处理器,如使用 .andDo(MockMvcResultHandlers.print()) 输出整个响应结果信息,可以在调试的时候使用。
-
ResultActions.andReturn:表示执行完成后返回相应的结果
2.5 MvcResult
执行完控制器后得到的整个结果,并不仅仅是返回值,其包含了测试时需要的所有信息。
* MockHttpServletRequest getRequest():得到执行的请求;
* MockHttpServletResponse getResponse():得到执行后的响应;
* Object getHandler():得到执行的处理器,一般就是控制器;
* HandlerInterceptor[] getInterceptors():得到对处理器进行拦截的拦截器;
* ModelAndView getModelAndView():得到执行后的ModelAndView;
* Exception getResolvedException():得到HandlerExceptionResolver解析后的异常;
* FlashMap getFlashMap():得到FlashMap;
* Object getAsyncResult()/Object getAsyncResult(long timeout):得到异步执行的结果;
三、实例
3.1 引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
3.2 Get 请求
- 创建 Get 请求方法
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/hello")
public String hello(String name) {
return "hello " + name;
}
}
- 编写测试
@SpringBootTest
public class TestControllerTest {
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@BeforeEach
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
@Test
public void should_return_hello() throws Exception {
String expectedResult = "hello MarkLogZhu";
final String result = mockMvc.perform(
// GET 请求
get("/test/hello")
// 添加参数
.param("name", "MarkLogZhu")
)
// 断言 HTTP 返回状态是 200
.andExpect(status().isOk())
// 获取返回结果对象
.andReturn()
// 将响应值以字符串形式返回
.getResponse().getContentAsString();
assertThat(result, is(expectedResult));
}
}
3.3 POST 请求
- 编写 Post 请求
@RestController
@RequestMapping("/test")
public class TestController {
@PostMapping("/create")
public User create(@RequestBody CreateUserCommand command) {
return new User(command.getName(), command.getAge()+1);
}
}
public class CreateUserCommand {
private String name;
private Integer age;
public CreateUserCommand(){}
public CreateUserCommand(String name, Integer age) {
this.name = name;
this.age = age;
}
...... get/set
}
public class User {
private String name;
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
...... get/set
}
2) 编写测试
@SpringBootTest
public class TestControllerTest {
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@BeforeEach
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
@Test
public void should_create_user_success() throws Exception {
String expectedResult = "{"name":"MarkLogZhu","age":19}";
String requestJson = "{"name":"MarkLogZhu","age":18}";
final String result = mockMvc.perform(
// POST 请求
post("/test/create")
// 设置请求内容格式为JSON
.contentType(MediaType.APPLICATION_JSON)
// 请求内容
.content(requestJson)
)
// 获取返回结果对象
.andReturn()
// 将响应值以字符串形式返回
.getResponse()
.getContentAsString();
assertThat(result, is(expectedResult));
}
}