本文重点来写点代码,对spring boot中http接口的返回进行单测。我们在springboot单元测试实践1中,已经写了如何mockcontroller的请求,测试controller中的逻辑,并简单针对返回体response进行了简单的测试。
在实际的生产环境中,spring boot对外提供的http接口的返回,根据具体的业务场景可能是个很复杂的数据结构。目前,大多数前后端分离的项目,返回的内容都是一整段的json格式的数据。
所以,我们对response做测试,大部分就是在针对response中返回的json做校验,json中有可能返回的是array,map等嵌套的复杂结构。
新建http请求接口
在前文的DemoController中,新建几个http的请求接口
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
@Slf4j
public class DemoController {
@GetMapping("/log")
public String log() {
log.info("test print log");
return "log";
}
@GetMapping("/list")
public List<String> stringList() {
List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
return list;
}
@GetMapping("/map")
public Object mapList() {
List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
return Map.of("list", list);
}
@GetMapping("/result")
public Object resultList() {
Result result1 = new Result();
result1.setKey("key1");
Result result2 = new Result();
result2.setKey("key2");
return List.of(result1, result2);
}
@Getter
@Setter
private class Result {
private String key;
private String value;
}
}
内容中新增了/api/list 、 /api/map、 /api/result 三个接口
/api/list 的单测
/api/list 主要是用来模拟直接返回一个list的列表,也就是说实际的response是下面这个样子的
["one", "two", "three"]
针对 /api/list,写了下面的测试case
@Test
void stringList() throws Exception {
this.mockMvc
.perform(MockMvcRequestBuilders.get("/api/list"))
.andDo(print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.iterableWithSize(3)));
}
.andDo(print()) 实际上就是用来在控制台打印具体的response内容,这样方便比对response的内容进行针对性的测试
jsonPath方法是用来将response按照json的格式来进行解析,"$"符号代表着json的根对象,因为我们返回的是个list,所以根对象实际指的就是当前返回的array数组,这里我们期望返回的array列表的大小是3
/api/map 的单测
/api/map 主要是用来模拟直接返回一个map,也就是说实际的response是下面的这个样子的, 这里我们直接使用andDo(print())的控制台的打印来检查返回内容
针对 /api/map, 写了下面的测试case
@Test
void mapList() throws Exception {
this.mockMvc
.perform(MockMvcRequestBuilders.get("/api/map"))
.andDo(print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$").isMap())
.andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasKey("list")))
.andExpect(MockMvcResultMatchers.jsonPath("$.list", Matchers.iterableWithSize(3)))
.andExpect(MockMvcResultMatchers.jsonPath("$.list[0]", Matchers.is("one")));
}
首先,我们测试jsonPath拿到的根对象的内容是不是一个map,再来检查map返回的内容中是不是有一个list的key,再来检查list返回的结果的大小是不是3个,最后来获取list中第一个对象的内容
/api/result接口的单测
/api/result 主要是用来模拟直接返回一个对象的列表,我们直接使用andDo(print())的控制台的打印来检查返回内容
针对 /api/result,写了下面的测试case
@Test
void resultList() throws Exception {
this.mockMvc
.perform(MockMvcRequestBuilders.get("/api/result"))
.andDo(print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$").isArray())
.andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.iterableWithSize(2)))
.andExpect(
MockMvcResultMatchers.jsonPath(
"$", Matchers.everyItem(Matchers.allOf(Matchers.hasKey("key")))))
.andExpect(MockMvcResultMatchers.jsonPath("$[0].key", Matchers.is("key1")));
}
我们先测试jsonPath拿到的根对象是不是一个array数组,再检查列表中的大小是不是2,再检查列表中所有的对象,都包含key这个字段,最后检查列表中的第一个对象的key字段对应的值为key1
好了,针对json的测试目前也就这么多,更复杂的测试,可以多去看看MockMvcResultMatchers中提供的api和Matchers中提供的校验方法。
就到这里了,如果有复杂的场景,也可以在评论里写出来,一起来玩转业务场景的各种单测case