1. 概述

本教程中,我们将展示如何使用Mockito中的Spy

我们将讨论@Spy注解的使用以及如何对Spy进行打桩。最后,我们将深入理解MockSpy之间的区别。

当然,有关更多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. MockSpy`的区别

接下来,我们讨论MockSpy在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打桩,以及MockSpy之间的区别。

所有这些示例的实现可以在GitHub上中找到。

这是一个Maven项目,因此导入和运行应该很容易。

最后,想要了解更多Mockito的内容,请访问这个系列


« 上一篇: Baeldung周报44
» 下一篇: Spring Profiles