Mockito使用
本文混乱,待编辑优化……
本文参考 使用Mockito进行单元测试【1】——mock and verify、Mockito 中文文档 ( 2.0.26 beta )
Mockito的使用包括
- 1.Mockito mock---mock一个接口或类
- 2.Mockito stubbing---打桩功能stubbing 使方法调用返回期望的值
- 3.Mockito verfiry---verify验证,验证mock的接口中的方法是否被调用,被调用次数
- 4.Mockito argument matchers---在stubbing或者verify时,模拟传入的参数
- 5.Mockito.spy---Mock同一个类中的函数调用
1.Mockito---mock一个接口或类
创建一个类或接口的mock实例 是指这个mock实例拥有这个类或接口的所有方法,并且给这些方法以最基本的实现:如果是返回void,他什么都不做,否则他就返回null或0等基本类型的值。
a.创建一个类或接口的mock实例---使用mock()方法
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.List;
import org.junit.jupiter.api.Test;
public class MockitoTest {
@Test
public void test() throws Exception {
// 使用mock()方法 创建List接口的mock实例
List<String> list = mock(List.class);
list.add("xx");
verify(list, times(1)).add("xx");
verify(list, times(1)).add("y");
}
}
b.创建一个类或接口的mock实例---使用@MockBean注解
在SpringBoot Test中,@MockBean注解 可以创建接口的mock实例,被用来添加 mock 对象到 Spring ApplicationContext中
//@MockBean 创建接口的mock实例
//这个studentService实例就拥有StudentService接口的所有方法
//并且给这些方法以最基本的实现:如果是返回void,他什么都不做,否则他就返回null或0等基本类型的值。
@MockBean
private StudentService studentService;
2.Mockito---打桩功能stubbing 使方法调用返回期望的值
打桩是一个形象的说法,就是把所需的测试数据塞进对象中,适用于基于状态的(state-based)测试,关注的是输入和输出。
Mockito 中 when(xxxx).thenReturn(yyyy):在when中定义对象方法和参数(输入),然后在 thenReturn 中指定结果(输出)。此过程称为 Stub 打桩。
比如以下情况:controller 依赖service,调用service的findAll()方法获取Student的集合并且对获取到的Student集合 进行下一步处理时(把所有Student的name打印到控制台),这时候就可以用mockito的打桩功能,给service的findAll()方法返回的是个固定值。然后在controller中继续处理。
一旦这个方法被 stub 了,就会一直返回这个 stub 的值。
// mock data
List<Student> students = new ArrayList<Student>();
Student student1 = new Student();
student1.setName("xxx");
students.add(student1);
Student student2 = new Student();
student2.setName("yyy");
students.add(student2);
// 打桩 stub
Mockito.when(studentService.findAll()).thenReturn(students);
3.Mockito---verify验证
验证:
- 有返回结果的方法:可以通过打桩这些方法,然后调用方法,判断返回值是不是 用来打桩的值;也可以直接判断这个方法有没有被调用过。
- 没有返回结果,void类型的方法:只能通过是否被触发执行来做相应的判断。对于 void 返回值的方法一般可不用去 mock 它,只需用 verify() 去验证。参考Mockito 如何 mock 返回值为 void 的方法
Mockito提供了verify()方法来验证这个mock实例的某个方法是否被调用,调用了几次,是否带有参数以及带有何种参数等。
// 验证方法执行过一次
verify(studentService, times(1)).findAll();
//最少调用一次
verify(studentService,atLeastOnce()).findAll();
//最多调用一次
verify(studentService,atMostOnce()).findAll();
//从没调用过
verify(studentService,never()).findAll();
4.Mockito---模拟传入的参数 argument matchers
有时候要stubbing或者verify的方法有参数,但是我不关心输入的具体内容。只是完成打桩或者验证。这时候就可以用Mockito提供的argument matchers 机制,
例如 anyString() 匹配任何 String 参数,anyInt() 匹配任何 int 参数,anySet() 匹配任何 Set,any() 则意味着参数为任意值。
模拟参数可以用在两种情况下:打桩时模拟传入的参数 验证时模拟传入的参数
package com.example.junittestdemo;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@EnableWebMvc
@AutoConfigureMockMvc
@SpringBootTest(classes = StudentController.class)
public class StudentControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private StudentService studentService;
@Test
public void testfindById() throws Exception {
// mock data
Student student = new Student();
student.setName("mm");
// stubbing 传入参数any()
Mockito.when(studentService.findById(any())).thenReturn(student);
// stubbing 传入参数anyLong()
Mockito.when(studentService.findById(anyLong())).thenReturn(student);
// stubbing 传入参数90
Mockito.when(studentService.findById(new Long(10))).thenReturn(student);
// 执行一个get请求
mockMvc.perform(MockMvcRequestBuilders.post("/student/find")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content("90"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print());
}
}
5.Mockito.spy ---Mock同一个类中的函数调用
一般都是根据单元测试规范设计程序,尽量避免同一个类中的函数引用。
但代码实际开发过程中,总有很多特殊情况。如果有同一个类之间的引用,在测试添加函数时就出现了麻烦事,该如何Mock 这个同一个类中的函数调用呢?
import org.springframework.stereotype.Service;
@Service
public class StudentServiceImpl implements StudentService {
public void test(Integer a, Integer b) {
System.out.println("this is test method begin");
Integer sum = add(a, b);
System.out.println("this is test method end = " + sum);
}
public Integer add(Integer a, Integer b) {
System.out.println("this is add method");
return a + b;
}
}
测试类
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(classes = StudentServiceImpl.class)
class StudentServiceImplTest {
@Autowired
private StudentServiceImpl studentService;
@Test
void testTest() {
//spy这个类
StudentServiceImpl spyStudentService = Mockito.spy(studentService);
//test
spyStudentService.test(3, 4);
//测试同一个类中的方法
Mockito.verify(spyStudentService, Mockito.times(1)).add(3, 4);
}
}
有需要测试的controller 如下
import java.io.IOException;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/student")
public class StudentController {
@Autowired
private StudentService studentService;
@GetMapping("/findAll")
public ResponseEntity findAll() throws IOException {
List<Student> students = studentService.findAll();
for (Student student : students) {
System.out.println("student name=" + student.getName());
}
return ResponseEntity.ok(students);
}
}
添加依赖
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-all -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>2.0.2-beta</version>
<scope>test</scope>
</dependency>
测试类如下
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@EnableWebMvc
@AutoConfigureMockMvc
@SpringBootTest(classes = StudentController.class)
public class StudentControllerTest {
@Autowired
private MockMvc mockMvc;
// @MockBean 创建接口的mock实例
// 这个studentService实例就拥有StudentService接口的所有方法
// 并且给这些方法以最基本的实现:如果是返回void,他什么都不做,否则他就返回null或0等基本类型的值。
@MockBean
private StudentService studentService;
@Test
public void testFindAll() throws Exception {
// mock data
List<Student> students = new ArrayList<Student>();
Student student1 = new Student();
student1.setName("xxx");
students.add(student1);
Student student2 = new Student();
student2.setName("yyy");
students.add(student2);
//mockito stub 打桩
Mockito.when(studentService.findAll()).thenReturn(students);
//测试
mockMvc.perform(MockMvcRequestBuilders.get("/student/findAll"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print());
// 验证方法执行过一次
verify(studentService, times(1)).findAll();
}
}
运行测试类,控制台输出
student name=xxx
student name=yyy