Mockito – Using Spies – Mockito – 使用间谍

最后修改: 2014年 11月 7日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll illustrate how to make the most out of spies in Mockito.

在本教程中,我们将说明如何在Mockito中充分利用spies

We’ll talk about the @Spy annotation and how to stub a spy. Finally, we’ll go into the difference between Mock and Spy.

我们将讨论@Spy注解以及如何存根一个spy。最后,我们将讨论MockSpy之间的区别。

Of course, for more Mockito goodness, have a look at the series here.

当然,对于更多Mockito的好处,看一下这里的系列

2. Simple Spy Example

2.简单的间谍例子

Let’s start with a simple example of how to use a spy.

让我们从一个简单的例子开始如何使用间谍

Simply put, the API is Mockito.spy() to spy on a real object.

简单地说,该API是Mockito.spy()窥探一个真实的对象

This will allow us to call all the normal methods of the object while still tracking every interaction, just as we would with a mock.

这将允许我们调用该对象的所有正常方法,同时仍然跟踪每一个交互,就像我们对一个模拟对象那样。

Now let’s do a quick example where we’ll spy on an existing ArrayList object:

现在让我们做一个快速的例子,我们将监视一个现有的ArrayList对象。

@Test
public void whenSpyingOnList_thenCorrect() {
    List<String> list = new ArrayList<String>();
    List<String> spyList = Mockito.spy(list);

    spyList.add("one");
    spyList.add("two");

    Mockito.verify(spyList).add("one");
    Mockito.verify(spyList).add("two");

    assertThat(spyList).hasSize(2);
}

Note how the real method add() is actually called and how the size of spyList becomes 2.

请注意真正的方法add()是如何被调用的以及spyList的大小如何变成2。

3. The @Spy Annotation

3.@Spy注释

Next, let’s see how to use the @Spy annotation. We can use the @Spy annotation instead of spy():

接下来,让我们看看如何使用@Spy注解。我们可以使用@Spy注解来代替spy()

@Spy
List<String> spyList = new ArrayList<String>();

@Test
public void whenUsingTheSpyAnnotation_thenObjectIsSpied() {
    spyList.add("one");
    spyList.add("two");

    Mockito.verify(spyList).add("one");
    Mockito.verify(spyList).add("two");

    assertThat(aSpyList).hasSize(2);
}

To enable Mockito annotations (such as @Spy, @Mock, … ), we need to do one of the following:

为了启用Mockito注解(如@Spy, @Mock, …),我们需要做以下工作之一。

  • Call the method MockitoAnnotations.initMocks(this) to initialize annotated fields
  • Use the built-in runner @RunWith(MockitoJUnitRunner.class)

4. Stubbing a Spy

4.刺杀间谍

Now let’s see how to stub a Spy. We can configure/override the behavior of a method using the same syntax we would use with a mock.

现在让我们看看如何存根一个Spy。我们可以使用与mock相同的语法来配置/重写一个方法的行为。

Here we’ll use doReturn() to override the size() method:

这里我们将使用doReturn()来重写size()方法。

@Test
public void whenStubASpy_thenStubbed() {
    List<String> list = new ArrayList<String>();
    List<String> spyList = Mockito.spy(list);

    assertEquals(0, spyList.size());

    Mockito.doReturn(100).when(spyList).size();
    assertThat(spyList).hasSize(100);
}

5. Mock vs Spy in Mockito

5.MockMockito中的Spy

Let’s discuss the difference between Mock and Spy in Mockito. We won’t examine the theoretical differences between the two concepts, just how they differ within Mockito itself.

让我们来讨论一下Mockito中MockSpy之间的区别。我们不会研究这两个概念之间的理论差异,只是研究它们在Mockito本身中的区别。

When Mockito creates a mock, it does so from the Class of a Type, not from an actual instance. The mock simply creates a bare-bones shell instance of the Class, entirely instrumented to track interactions with it.

当Mockito创建一个模拟时,它是从一个类型的Class开始,而不是从一个实际的实例开始。该模拟只是创建了一个类的裸壳实例,完全是为了跟踪与它的交互。

On the other hand, the spy will wrap an existing instance. It will still behave in the same way as the normal instance; the only difference is that it will also be instrumented to track all the interactions with it.

另一方面,间谍将包裹一个现有的实例。它的行为仍然与正常的实例相同;唯一的区别是,它也将被仪器化,以跟踪与它的所有交互。

Here we’ll create a mock of the ArrayList class:

这里我们将创建一个mockArrayList类。

@Test
public void whenCreateMock_thenCreated() {
    List mockedList = Mockito.mock(ArrayList.class);

    mockedList.add("one");
    Mockito.verify(mockedList).add("one");

    assertThat(mockedList).hasSize(0);
}

As we can see, adding an element into the mocked list doesn’t actually add anything; it just calls the method with no other side effects.

正如我们所看到的,在模拟列表中添加一个元素实际上并没有添加任何东西;它只是调用了这个方法,没有其他的副作用。

A spy, on the other hand, will behave differently; it will actually call the real implementation of the add method and add the element to the underlying list:

另一方面,间谍会有不同的表现;它实际上会调用add方法的真正实现,并将该元素添加到底层列表中。

@Test
public void whenCreateSpy_thenCreate() {
    List spyList = Mockito.spy(new ArrayList());

    spyList.add("one");
    Mockito.verify(spyList).add("one");

    assertThat(spyList).hasSize(1);
}

6. Understanding the Mockito NotAMockException

6.了解Mockito NotAMockException

In this final section, we’ll learn about the Mockito NotAMockException. This exception is one of the common exceptions we will likely encounter when misusing mocks or spies.

在最后一节,我们将学习Mockito的NotAMockException这个异常是我们在滥用mock或spies时可能遇到的常见异常之一

Let’s start by understanding the circumstances in which this exception can occur:

让我们先来了解一下这种例外情况可能发生的情况。

List<String> list = new ArrayList<String>();
Mockito.doReturn(100).when(list).size();

When we run this code snippet, we’ll get the following error:

当我们运行这个代码段时,我们会得到以下错误。

org.mockito.exceptions.misusing.NotAMockException: 
Argument passed to when() is not a mock!
Example of correct stubbing:
    doThrow(new RuntimeException()).when(mock).someMethod();

Thankfully, it is quite clear from the Mockito error message what the problem is here. In our example, the list object is not a mock. The Mockito when() method expects a mock or spy object as the argument.

值得庆幸的是,从Mockito的错误信息中可以很清楚地看到这里的问题所在。在我们的例子中,list对象不是一个模拟对象。Mockito的when()方法期望一个mock或spy对象作为参数

As we can also see, the Exception message even describes what a correct invocation should look like. Now that we have a better understanding of what the problem is, let’s fix it by following the recommendation:

我们还可以看到,异常消息甚至描述了一个正确的调用应该是什么样子的。现在我们对问题有了更好的理解,让我们按照建议来解决它。

final List<String> spyList = Mockito.spy(new ArrayList<>());
assertThatNoException().isThrownBy(() -> Mockito.doReturn(100).when(spyList).size());

Our example now behaves as expected, and we no longer see the Mockito NotAMockException.

我们的例子现在表现得和预期一样,我们不再看到Mockito的NotAMockException.

7. Conclusion

7.结论

In this brief article, we discussed the most useful examples of using Mockito spies.

在这篇简短的文章中,我们讨论了使用Mockito间谍的最有用的例子。

We learned how to create a spy, use the @Spy annotation, stub a spy, and finally, the difference between Mock and Spy.

我们学习了如何创建一个spy,使用@Spy注解,存根一个spy,,最后是MockSpy的区别。

The implementation of all of these examples can be found over on GitHub.

所有这些例子的实现都可以在GitHub上找到over

This is a Maven project, so it should be easy to import and run as it is.

这是一个Maven项目,所以应该很容易导入并按原样运行。

Finally, for more Mockito goodness, have a look at the series here.

最后,对于更多Mockito的好处,看一下这里的系列