1. 概述
本教程中,我们将展示如何使用Mockito中的Spy。
我们将讨论@Spy
注解的使用以及如何对Spy进行打桩。最后,我们将深入理解Mock
与Spy
之间的区别。
当然,有关更多Mockito的精彩内容,可以参考这里的一系列教程。
2. 简单的Spy
示例
首先,让我们来看一个简单的Spy
使用示例。
简单来说,使用 Mockito.spy()
用于监视真实对象。
这使得我们可以调用对象普通方法的同时,支持像mock一样跟踪每个交互。
现在,让我们通过一个快速的例子来演示如何对现有的ArrayList
对象进行Spy:
@Test
void givenUsingSpyMethod_whenSpyingOnList_thenCorrect() {
List<String> list = new ArrayList<String>();
List<String> spyList = spy(list);
spyList.add("one");
spyList.add("two");
verify(spyList).add("one");
verify(spyList).add("two");
assertThat(spyList).hasSize(2);
}
注意,这里调用的是真实的add()
方法,spyList
的大小变成了2。
3. @Spy
注解
下面使用@Spy
注解实现相同的功能,替代spy()
方法:
@Spy
List<String> spyList = new ArrayList<String>();
@Test
void givenUsingSpyAnnotation_whenSpyingOnList_thenCorrect() {
spyList.add("one");
spyList.add("two");
verify(spyList).add("one");
verify(spyList).add("two");
assertThat(aSpyList).hasSize(2);
}
为了启用Mockito注解(如@Spy
、@Mock
等),我们需要使用@ExtendWith(MockitoExtension.class)
,进行初始化。
4. Spy 打桩
现在,让我们看看如何对Spy
进行打桩。我们可以使用与mock相同的语法来配置或覆盖方法的行为。
这里我们将使用doReturn()
来重写size()
方法:
@Test
void givenASpy_whenStubbingTheBehaviour_thenCorrect() {
List<String> list = new ArrayList<String>();
List<String> spyList = spy(list);
assertEquals(0, spyList.size());
doReturn(100).when(spyList).size();
assertThat(spyList).hasSize(100);
}
5. Mock与
Spy`的区别
接下来,我们讨论Mock
和Spy
在Mockito中的区别。我们不会探讨这两个概念的理论差异,只是关注它们在Mockito本身中的不同。
当Mockito创建mock时,它是根据Class类型创建的,而不是真实的对象实例。mock只是创建了一个完全受监控的类的外壳实例,用于追踪与其的交互。
相反,Spy会包装一个现有的实例。它仍然以与普通实例相同的方式行为;唯一不同的是,它也将被检测以跟踪与它的所有交互。
这里我们将创建一个ArrayList
类的mock
:
@Test
void whenCreateMock_thenCreated() {
List mockedList = mock(ArrayList.class);
mockedList.add("one");
verify(mockedList).add("one");
assertThat(mockedList).hasSize(0);
}
如我们所见,向mock列表中添加元素实际上并没有添加任何内容;它只是调用了方法,没有其他副作用。
另一方面,Spy的行为会有所不同;它实际上会调用原始的add
方法,并将元素添加到底层列表中:
@Test
void whenCreateSpy_thenCreate() {
List spyList = Mockito.spy(new ArrayList());
spyList.add("one");
Mockito.verify(spyList).add("one");
assertThat(spyList).hasSize(1);
}
6. 理解Mockito的NotAMockException
在本节中,我们将学习关于Mockito的NotAMockException
。这个异常是我们可能会在滥用mock或spy时遇到的常见异常之一。
首先,让我们理解这种异常可能发生的情况:
List<String> list = new ArrayList<String>();
doReturn(100).when(list).size();
运行这段代码片段时,我们会得到以下错误:
org.mockito.exceptions.misusing.NotAMockException:
Argument passed to when() is not a mock!
Example of correct stubbing:
doThrow(new RuntimeException()).when(mock).someMethod();
幸运的是,Mockito的错误消息相当清楚地说明了问题所在。在我们的例子中,list
对象不是一个mock。Mockito的when()
方法期望其参数是mock或spy对象。
我们还可以看到,异常消息甚至描述了正确调用应该如何。现在我们对问题有了更好的理解,让我们按照推荐的方式修复它:
final List<String> spyList = spy(new ArrayList<>());
assertThatNoException().isThrownBy(() -> doReturn(100).when(spyList).size());
我们的示例现在按预期工作,我们不再看到NotAMockException
异常。
7. 总结
本文, 我们学会了如何创建Spy
,使用@Spy
注解,Spy
打桩,以及Mock
与Spy
之间的区别。
所有这些示例的实现可以在GitHub上中找到。
这是一个Maven项目,因此导入和运行应该很容易。
最后,想要了解更多Mockito的内容,请访问这个系列。