前言
Mockito
是当前最流行的单元测试Mock
框架。我们可以虚拟出一个外部依赖,降低测试组件之间的耦合度,只注重代码的流程与结果,真正地实现测试目的。
什么是Mockito
Mock
的中文译为仿制的,模拟的,虚假的。对于测试框架来说,即构造出一个模拟/虚假的对象,使我们的测试能顺利进行下去。
ito
其实是input to output
Mockito
大致意思就是模拟输入和输出,用一个虚拟的对象(Mock
对象)来创建,以便测试方法。
为什么使用Mock测试
单元测试是为了验证我们的代码运行正确性,我们注重的是代码的流程以及结果的正确与否。
对比真实运行代码,可能其中有一些外部依赖的构建步骤相对麻烦,测试用例显得复杂难懂,会大大增加单元测试的工作。
使用Mock
,我们可以虚拟出一个外部依赖,只注重代码的流程与结果,真正地实现测试目的。
使用Mock测试的好处
- 可以很简单的虚拟出一个复杂对象(比如虚拟出一个接口的实现类);
- 可以使测试用例只注重测试流程与结果;
- 减少外部类、系统和依赖给单元测试带来的耦合。
- 团队可以并行工作(比如
A
依赖B
,但B
并没有开发,此时就可以Mock
出一个B
)
使用Mockito测试示例
C层代码:
@RestController
@RequestMapping("Student")
public class StudentController {
@Autowired
StudentService studentService;
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Student save(@RequestBody Student student) {
return studentService.save(student);
}
}
M层代码:
-
接口
public interface StudentService { Student save(Student student); }
-
实现类:
@Service public class StudentServiceImpl implements StudentService { @Autowired StudentRepository studentRepository; @Override public Student save(Student student) { return studentRepository.save(student); } }
以save这个方法为例,进行C
M
层测试
测试C层:
- 模拟发送请求
- 测试传入的值是否符合预期
- 测试返回的值是否符合预期
public class StudentControllerTest {
@Test
public void save() throws Exception {
// 模拟发送请求
logger.info("准备输入数据");
String url = "Student";
JSONObject studentJsonObject = new JSONObject();
JSONObject klassJsonObject = new JSONObject();
studentJsonObject.put("sno", "学号测试");
studentJsonObject.put("name", "姓名测试");
klassJsonObject.put("id", -1);
studentJsonObject.put("klass", klassJsonObject);
logger.info("发起请求");
MvcResult mvcResult = this.mockMvc.perform(
MockMvcRequestBuilders.post(url)
.content(studentJsonObject.toString()
.contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(MockMvcResultMatchers.status().is(201)
.andDo(MockMvcResultHandlers.print())
.andReturn();
// 测试传入的值是否符合预期
logger.info("新建参数捕获器");
ArgumentCaptor<Student> studentArgumentCaptor = ArgumentCaptor.forClass(Student.class);
Mockito.verify(studentService).save(studentArgumentCaptor.capture());
Student passedStudent = studentArgumentCaptor.getValue();
logger.info("断言捕获的对与我们前面传入的值的相同");
Assertions.assertThat(passedStudent.getSno()).isEqualTo("学号测试");
Assertions.assertThat(passedStudent.getName()).isEqualTo("姓名测试");
Assertions.assertThat(passedStudent.getId()).isNull();
Assertions.assertThat(passedStudent.getKlass().getId()).isEqualTo(-1L);
// 测试返回的值是否符合预期
logger.info("准备服务层被调用后的返回数据");
Student returnStudent = new Student();
returnStudent.setId(1L);
returnStudent.setSno("测试返回学号");
returnStudent.setName("测试返回姓名");
returnStudent.setKlass(new Klass());
returnStudent.getKlass().setId(1L);
Mockito.when(studentService.save(Mockito.any(Student.class))).thenReturn(returnStudent);
logger.info("获取返回的值并断言此值与我们模拟的返回值相同");
String stringReturn = mvcResult.getResponse().getContentAsString();
DocumentContext documentContext = JsonPath.parse(stringReturn);
LinkedHashMap studentHashMap = documentContext.json();
Assertions.assertThat(studentHashMap.get("id")).isEqualTo(1);
Assertions.assertThat(studentHashMap.get("sno")).isEqualTo("测试返回学号");
Assertions.assertThat(studentHashMap.get("name")).isEqualTo("测试返回姓名");
LinkedHashMap klassHashMap = (LinkedHashMap) studentHashMap.get("klass");
Assertions.assertThat(klassHashMap.get("id")).isEqualTo(1);
}
}
测试M层:
- M层单元测试相对比较简单,不需要模拟前台的数据,逻辑更加清晰
public class StudentServiceImplTest {
@Test
public void save() {
logger.info("准备传入的数据");
Student passStudent = new Student();
logger.info("调用服务层");
Student returnStudent = studentService.save(passStudent);
logger.info("准备返回的数据");
Student mockReturnStudent = new Student();
Mockito.when(studentRepository.save(Mockito.any(Student.class))).thenReturn(mockReturnStudent);
logger.info("新建参数捕获器");
ArgumentCaptor<Student> studentArgumentCaptor =ArgumentCaptor.forClass(Student.class);
Mockito.verify(studentRepository).save(studentArgumentCaptor.capture());
logger.info("断言获取的数据与传入的相同");
Assertions.assertThat(studentArgumentCaptor.getValue()).isEqualTo(passStudent);
logger.info("断言返回的数据与输出的相同");
Assertions.assertThat(returnStudent).isEqualTo(mockReturnStudent);
}
}
总结
总感觉当时能搞明白啥意思,但是时间一长自己还是有点懵,还需要捋一下思路才行,所有在此简单记录总结一下。
参考SpringBoot+Angular入门实例教程[4.5.9] 关于更多详细的SpringBoot
与 Angular
的知识教程都有提到,有兴趣的可以阅读。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。