👍mockito测试框架
1.mock简介
1-1.mock的引入
测试驱动的开发(Test Driven Design, TDD)要求我们先写单元测试,再写实现代码。
而类可能有很多依赖,这些依赖的类/对象/资源又有别的依赖,从而形成很深的依赖树。要在单元测试的环境中完整地构建这样的依赖,是一件很困难的事情。
为了测试类A,我们需要Mock B类和C类
Mockito是一个模拟测试框架,可以让你用优雅,简洁的接口写出漂亮的单元测试。Mockito可以让单元测试易于可读,产生简洁的校验错误。
1-2.特点
- 提前创建测试,TDD(测试驱动开发)
- 团队可以并行工作
- 你可以创建一个验证或者演示程序
- 为无法访问的资源编写测试
- Mock可以交给用户
- 隔离系统
1-3.mock和stub概念
Mock
所谓的mock,即模拟,模仿的意思。Mock 技术的主要作用是使用mock工具模拟一些在应用中不容易构造或者比较复杂的对象,从而把测试目标与测试边界以外的对象隔离开。
Stub
Stub(桩)。单元测试过程中,对于在应用中不容易构造或者比较复杂的对象,用一个虚拟的对象来代替它。stub的方法也会有具体的实现,哪怕简单到只有一个简单的return语句。
从类的实现方式上看,stub有一个显式的类实现,按照stub类的复用层次可以分类
- 普通类(被多个测试案例复用)
- 内部类(被同一个测试案例的多个测试方法复用)
- 内部匿名类(只用于当前测试方法)。
Stub 与 Mock 的区别
Stub 是在单元测试过程中去代替某些对象来提供所需的测试数据,适用于基于状态的(state-based)测试,关注的是输入和输出。而Mock适用于基于交互的(interaction-based)测试,关注的是交互过程,不只是模拟状态,还能够模拟模块或对象的行为逻辑并能验证其正确性,Mock不需要类的显示实现,直接用工具模拟。
1-4.mock框架
目前流行的mock框架
EasyMock
不是本片的重点,自己科普下吧
Mockito
Mockito也是一个开源的mock工具包,和EasyMock不同的时,它不需要录制、播放这些动作,语法上更灵活,可读性更强
PowerMock
下篇讲解
本篇基于test-ng来使用mockito的
代码坐标在:code目录下,
testng-mockito
子项目中。
2.业务类
pom依赖
<dependencies>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.14.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
</dependencies>
实体类
public class Employee {
private int empno;
private String ename;
private Date hiredate;
private double sal;
...省略setter、getter、toString、构造方法...
}
dao
public class EmployeeDao {
public void addEmp(Employee employee) {
System.out.println("EmployeeDao--addEmp: " + employee);
}
public List<Employee> listEmps() {
System.out.println("EmployeeDao--list");
Employee emp = new Employee(1, "沐雨云楼", new Date(), 20000);
return Collections.singletonList(emp);
}
public List<Employee> getByMap(Map<String, Object> paramMap) {
System.out.println("EmployeeDao--getByMap: " + paramMap);
Employee emp = new Employee(1, "沐雨云楼", new Date(), 20000);
return Collections.singletonList(emp);
}
}
service
public class EmployeeService {
private EmployeeDao employeeDao;
public void addEmp(Employee employee) {
employeeDao.addEmp(employee);
System.out.println("添加成功");
}
public void listEmps() {
List<Employee> employees = employeeDao.listEmps();
System.out.println(employees);
}
public List<Employee> getByMap(Map<String, Object> paramMap) {
return employeeDao.getByMap(paramMap);
}
}
3.简单测试
3-1. 正常打桩
public class ListTest {
@Test
public void test1(){
LinkedList mockedList = Mockito.mock(LinkedList.class);
Mockito.when(mockedList.get(0)).thenReturn("first");
// 返回first
System.out.println(mockedList.get(0));
// 返回 null
System.out.println(mockedList.get(2));
}
}
mock
方法,可以创建Mock对象when--thenReturn
方式,进行打桩
3-2.异常打桩
@Test(expectedExceptions = RuntimeException.class)
public void test2(){
LinkedList mockedList = Mockito.mock(LinkedList.class);
Mockito.when(mockedList.get(0)).thenReturn("first");
Mockito.when(mockedList.get(1)).thenThrow(new RuntimeException());
// 返回first
System.out.println(mockedList.get(0));
// 返回RuntimeException
System.out.println(mockedList.get(1));
}
when--thenReturn
方式,进行异常打桩
3-3.mock参数
@Test
public void test3(){
LinkedList mockedList = Mockito.mock(LinkedList.class);
// Mockito.anyInt()
Mockito.when(mockedList.get(Mockito.anyInt())).thenReturn("first");
// 返回first
System.out.println(mockedList.get(0));
// 返回first
System.out.println(mockedList.get(1));
}
使用
Mockito.anyInt()
来定义参数值。除了anyInt()
外,还有anyString()
,anyMap()
等
3-4.多个返回值
@Test
public void test4(){
LinkedList mockedList = Mockito.mock(LinkedList.class);
Mockito.when(mockedList.get(Mockito.anyInt())).thenReturn("first").thenReturn("second");
// first
System.out.println(mockedList.get(0));
// second
System.out.println(mockedList.get(1));
// 后面都返回最后一个值 second
System.out.println(mockedList.get(2));
}
当有多个返回值时,注意返回值
3-5.doXxx打桩
@Test
public void test5(){
LinkedList mockedList = Mockito.mock(LinkedList.class);
Mockito.doReturn("first").doReturn("second").when(mockedList).get(Mockito.anyInt());
// first
System.out.println(mockedList.get(0));
// second
System.out.println(mockedList.get(1));
// 后面都返回最后一个值 second
System.out.println(mockedList.get(2));
}
使用
doReturn--when--method
进行打桩
当多个返回值时
@Test
public void test6(){
LinkedList mockedList = Mockito.mock(LinkedList.class);
Mockito.doReturn("first").doReturn("second").when(mockedList).get(0);
// first
System.out.println(mockedList.get(0));
// second
System.out.println(mockedList.get(0));
// 后面都返回最后一个值 second
System.out.println(mockedList.get(0));
// null
System.out.println(mockedList.get(2));
}
void返回值
@Test
public void test7(){
LinkedList mockedList = Mockito.mock(LinkedList.class);
// void 打桩
Mockito.doNothing().when(mockedList).push(0);
// void
mockedList.push(0);
// first
System.out.println(mockedList.get(0));
}
使用doNothing来打桩方法返回值为
void
的方法
3-6.verify
无序校验
@Test
public void test8(){
LinkedList mockedList = Mockito.mock(LinkedList.class);
// 操作
mockedList.get(0);
mockedList.clear();
// 验证之前的操作, 无顺序校验
Mockito.verify(mockedList).clear();
Mockito.verify(mockedList).get(0);
}
使用
verify
对方法进行前面mock对象,一些操作校验
次数校验
@Test
public void test10() {
LinkedList mockedList = Mockito.mock(LinkedList.class);
// 操作
mockedList.get(0);
mockedList.get(1);
mockedList.get(1);
mockedList.clear();
// 验证之前的操作, 有顺序校验 (交换下面顺序,校验不过)
// 至少一次
Mockito.verify(mockedList, Mockito.atLeastOnce()).get(0);
// 指定至少一次参数
// inOrder.verify(mockedList,Mockito.atLeast(1)).get(0);
// 没有
Mockito.verify(mockedList, Mockito.never()).get(100);
// 最多三次
Mockito.verify(mockedList, Mockito.atMost(3)).get(1);
Mockito.verify(mockedList).clear();
}
有序校验
@Test
public void test9(){
LinkedList mockedList = Mockito.mock(LinkedList.class);
// 操作
mockedList.get(0);
mockedList.clear();
InOrder inOrder = Mockito.inOrder(mockedList);
// 验证之前的操作, 有顺序校验 (交换下面顺序,校验不过)
inOrder.verify(mockedList).get(0);
inOrder.verify(mockedList).clear();
}
4.注解测试
@Captor
: 简化 ArgumentCaptor 的创建 - 当需要捕获的参数是一个令人讨厌的通用类,而且你想避免编译时警告。@Spy
: 你可以用它代替spy(Object)
方法@InjectMocks
: 自动将模拟对象或侦查域注入到被测试对象中。
所有新的注解仅仅在
MockitoAnnotations.initMocks(Object)
方法中被处理
注意
@InjectMocks 也能与 @Spy 一起使用,这就意味着Mockito会注入模拟对象到测试的部分测试中。它的复杂度也是应该使用部分测试原因。
4-1.Mock
之前是通过Mockito.mock
方法创建的mock对象。还可以通过自动注解方式来创建mock对象
public class AnnoListTest {
@Mock
private LinkedList mockedList;
@BeforeClass
public void init() {
MockitoAnnotations.initMocks(this);
}
@Test
public void test1() {
Mockito.doReturn("first").when(mockedList).get(Mockito.anyInt());
Assert.assertEquals("first", mockedList.get(0));
}
}
@Mock
: 定义需要mock的对象MockitoAnnotations.initMocks(this)
: 对当前对象进行mock初始化
4-2.InjectMocks
public class EmployeeServiceTest {
@InjectMocks
private EmployeeService employeeService;
@Mock
private EmployeeDao employeeDao;
@BeforeClass
private void init(){
MockitoAnnotations.initMocks(this);
}
@Test
public void testListEmps2() {
List<Employee> empList =new ArrayList<>();
empList.add(new Employee(3,"亚瑟",null,2));
empList.add(new Employee(4,"安其拉",null,3));
Mockito.doReturn(empList).when(employeeDao).listEmps();
employeeService.listEmps();
}
}
@InjectMocks
: 指定当前要测试的对象@Mock
: 指定当前要mock的对象
5.高级测试
5-1.doAnswer
平常使用thenReturn()
,thenThrow()
打桩就能满足测试。除此外,mockito还支持泛型接口Answer打桩。
public class AnswerTest {
@Mock
private EmployeeDao employeeDao;
@InjectMocks
private EmployeeService employeeService;
@BeforeClass
public void init() {
MockitoAnnotations.initMocks(this);
}
@Test
public void test1() {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("name", "海底小纵队");
List<Employee> empList = new ArrayList<>(1);
empList.add(new Employee(5, "呱唧", null, 2));
empList.add(new Employee(6, "巴克队长", null, 3));
Mockito.doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
Object[] args = invocationOnMock.getArguments();
System.out.println("调用参数:" + Arrays.toString(args));
Method method = invocationOnMock.getMethod();
System.out.println("调用方法:" + method.getName());
Object mock = invocationOnMock.getMock();
System.out.println("调用对象类型:" + mock.getClass().getSimpleName());
return empList;
}
}).when(employeeDao).getByMap(paramMap);
List<Employee> result = employeeService.getByMap(paramMap);
System.out.println(result);
}
}
5-2.spy
注意结论
- mock的对象的方法调用,不会调用真实对象的。
- spy的对象方法调用,会调用真实对象的。
mock测试
@Test
public void test1() {
List mock = Mockito.mock(LinkedList.class);
// 打桩
Mockito.when(mock.size()).thenReturn(100);
// 调用的是mock,不会往对象里添加元素
mock.add("one");
mock.add("two");
// null
System.out.println(mock.get(0));
// 100, 前面打桩了
System.out.println(mock.size());
// 验证
Mockito.verify(mock).add("one");
Mockito.verify(mock).add("two");
}
spy测试
@Test
public void test2() {
List list = new LinkedList();
List spy = Mockito.spy(list);
// 打桩
Mockito.when(spy.size()).thenReturn(100);
// 调用对象的真正的方法
spy.add("one");
spy.add("two");
// one
System.out.println(spy.get(0));
// 100, 前面打桩了
System.out.println(spy.size());
// 验证
Mockito.verify(spy).add("one");
Mockito.verify(spy).add("two");
}
常用的暂时就介绍这么多,更多的可以参照官网。
6.扩展链接
转载请注明来源,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 157162006@qq.com
文章标题:👍mockito测试框架
字数:2.4k
本文作者:沐雨云楼
发布时间:2020-08-23, 10:45:17
最后更新:2020-09-12, 21:21:47
原始链接:https://iworkh.gitee.io/blog/2020/08/23/java-mockito/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。