网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
Mockito
介绍
Mockito 是一个简单的流行的 Mock 框架。它能够帮我们创建 Mock 对象,保持单元测试的独立性。
使用它只需要在 Maven 中添加依赖即可。
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-all -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>2.0.2-beta</version>
</dependency>
创建 Mock 对象
通过方法创建
class CreateMock {
@Before
public void setup() {
mockUserDao = mock(UserDao.class);
userService = new UserServiceImpl();
userService.setUserDao(mockUserDao);
}
}
通过注解创建
class CreateMock {
@Mock
UserDao mockUserDao;
@InjectMocks
private UserServiceImpl userService;
@Before
public void setUp() {
//初始化对象的注解
MockitoAnnotations.initMocks(this);
}
}
一个简单的例子:
package Mockito;
import org.junit.Assert;
import org.junit.Test;
import java.util.List;
import static org.mockito.Mockito.*;
public class MyTest {
@Test
public void myTest() {
/\* 创建 Mock 对象 \*/
List list = mock(List.class);
/\* 设置预期,当调用 get(0) 方法时返回 "111" \*/
when(list.get(0)).thenReturn("111");
Assert.assertEquals("asd", 1, 1);
/\* 设置后返回期望的结果 \*/
System.out.println(list.get(0));
/\* 没有设置则返回 null \*/
System.out.println(list.get(1));
/\* 对 Mock 对象设置无效 \*/
list.add("12");
list.add("123");
/\* 返回之前设置的结果 \*/
System.out.println(list.get(0));
/\* 返回 null \*/
System.out.println(list.get(1));
/\* size 大小为 0 \*/
System.out.println(list.size());
/\* 验证操作,验证 get(0) 调用了 2 次 \*/
verify(list, times(2)).get(0);
/\* 验证返回结果 \*/
String ret = (String)list.get(0);
Assert.assertEquals(ret, "111");
}
}
从上面代码能看出一般使用的步骤:
设置方法预期返回
通过 when(list.get(0)).thenReturn(“111”); 来设置当调用 list.get(0) 时返回 “111”,该方法就是 Stub,替换我们实际的操作。
验证方法调用
该方法验证 get(0) 方法调用的次数 verify(list, times(2)).get(0);,还可以设置是否调用过,调用时间等等。
验证返回值
Assert.assertEquals(ret, “111”); 方法验证 Mock 对象方法调用后的返回值是否达到预期。
Mockito 使用
设置 Mock 对象期望和返回值
/\* 表示第一次调用 someMethod() 返回 value1 第二次调用返回 value2 \*/
when(mock.someMethod()).thenReturn(value1).thenReturn(value2);
when(mock.someMethod()).thenReturn(value1, value2);
/\* 也可以设置两次 \*/
when(mock.someMethod()).thenReturn(value1);
when(mock.someMethod()).thenReturn(value2);
另外一种写法 doReturn()
/\* 表示第一次调用 someMethod() 返回 value1 第二次调用返回 value2 \*/
doReturn(value1).doReturn(value2).when(mock).someMethod();
/\* 若返回 void,则设置为 doNothing() \*/
doNothing().when(mock).someMethod();
对方法设定返回异常
/\* 当调用 someMethod() 方法时会抛出异常 \*/
when(mock.someMethod()).thenThrow(new RuntimeException());
/\* 对 void 方法设定 \*/
doThrow(new RuntimeException()).when(mock).someMethod();
参数匹配器
我们不一定要固定 Stub 调用时的参数,如 get(0)。可以通过参数匹配器来调用。
when(list.get(anyInt())).thenReturn("hello");
Mock 对象的行为验证
package Mockito;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.testng.annotations.Test;
import java.util.List;
import static org.mockito.Mockito.*;
/\*\*
\* Created by andy.wwh on 2016/7/18.
\*/
public class Behavior {
@Test
public void behaviorCheck() {
List mock1 = mock(List.class);
List mock2 = mock(List.class);
/\* 设置预期 \*/
when(mock1.get(0)).thenReturn("hello world");
when(mock1.get(1)).thenReturn("hello world");
when(mock2.get(0)).thenReturn("hello world");
mock1.get(0);
/\* 验证方法调用一次 \*/
verify(mock1).get(0);
mock1.get(0);
/\* 验证方法调用两次 \*/
verify(mock1, times(2)).get(0);
/\* 验证方法从未被调用过 \*/
verify(mock2, never()).get(0);
/\* 验证方法 100 毫秒内调用两次 \*/
verify(mock1, timeout(100).times(2)).get(anyInt());
/\* 设置方法调用顺序 \*/
InOrder inOrder = inOrder(mock1, mock2);
inOrder.verify(mock1, times(2)).get(0);
inOrder.verify(mock2, never()).get(1);
/\* 查询是否存在被调用,但未被 verify 验证的方法 \*/
verifyNoMoreInteractions(mock1, mock2);
/\* 验证 Mock 对象是否没有交发生 \*/
verifyZeroInteractions(mock1, mock2);
/\* 参数捕获器 \*/
ArgumentCaptor<Integer> argumentCaptor = ArgumentCaptor.forClass(Integer.class);
verify(mock1, times(2)).get(argumentCaptor.capture());
System.out.println("argument:" + argumentCaptor.getValue());
}
}
验证调用次数
verify(mock1, timeout(100).times(2)).get(anyInt());
除了代码中的方法,Mockito 还提供了
- never() 没有被调用,相当于times(0)
- atLeast(N) 至少被调用N次
- atLeastOnce() 相当于atLeast(1)
- atMost(N) 最多被调用N次
超时验证
通过 timeout 我们可以进行验证程序执行时间是否符合规则。
方法调用顺序
Inorder 可以验证方法调用的顺序
verifyNoMoreInteractions 和 verifyZeroInteractions
verifyNoMoreInteractions:查询是否存在被调用,但未被 verify 验证的方法
verifyZeroInteractions:verifyZeroInteractions
ArgumentCaptor 参数捕获器
可在验证时对方法的参数进行捕获,最后验证捕获的参数值。如果方法有多个参数都要捕获验证,那就需要创建多个ArgumentCaptor对象处理。
Spy 对象验证
Mock 操作的全是虚拟对象。即使我们设置了 when(list.get(0)).thenReturn(1),我们调用如 size() 方法返回的还是 0。Mockito 还给我们提供了一种对真实对象操作的方法——Spy
做一个简单的比较:
package Mockito;
import org.testng.annotations.Test;
import java.util.List;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/\*\*
\* Created by andy.wwh on 2016/7/18.
\*/
public class MockObject {
@Test
public void MockTest() {
List list = mock(List.class);
when(list.get(0)).thenReturn("hello world");
System.out.println(list.get(0));
System.out.println(list.size());
}
}
package Mockito;
import org.testng.annotations.Test;
import java.util.LinkedList;
import java.util.List;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
/\*\*
\* Created by andy.wwh on 2016/7/18.
\*/
public class MockObject {
@Test
public void MockTest() {
/\* 创建真实对象 \*/
List list = new LinkedList();
List spy = spy(list);
spy.add("hello");


**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**
**[如果你需要这些资料,可以戳这里获取](https://bbs.youkuaiyun.com/topics/618668825)**
List list = new LinkedList();
List spy = spy(list);
spy.add("hello");
[外链图片转存中...(img-GFmexqlq-1715660719144)]
[外链图片转存中...(img-E34C1zOj-1715660719144)]
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**
**[如果你需要这些资料,可以戳这里获取](https://bbs.youkuaiyun.com/topics/618668825)**