Java Mockito - @Spy 与 @Mock

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28295625/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 05:57:48  来源:igfitidea点击:

Mockito - @Spy vs @Mock

javaunit-testingmockingmockitospy

提问by Abhinav

Mockito - I understand a spy calls the real methods on an object, while a mock calls methods on the double object. Also spies are to be avoided unless there is a code smell. However, how do spies work and when should i actually use them? How are they different from mocks?

Mockito - 我理解间谍调用对象上的真实方法,而模拟调用双对象上的方法。除非有代码异味,否则应避免间谍。但是,间谍是如何工作的,我应该什么时候真正使用它们?它们与模拟有什么不同?

回答by Jaimie Whiteside

The best place to start is probably the docs for mockito.

最好的起点可能是 mockito 的文档

On a general note the mockito mock allows you to create stubs.

总的来说,mockito 模拟允许您创建存根。

You would create a stub method if, for example, that method does an expensive operation. Say, it gets a database connection, retrieves a value from the database and returns it to the caller. Getting the db connection might take 30 seconds, slowing your test execution to the point where you'll likely context switch (or stop running the test).

例如,如果该方法执行昂贵的操作,您将创建一个存根方法。比如说,它获取一个数据库连接,从数据库中检索一个值并将其返回给调用者。获取数据库连接可能需要 30 秒,这会使您的测试执行速度减慢到您可能会进行上下文切换(或停止运行测试)的程度。

If the logic you are testing doesn't care about the database connection then you could replace that method with a stub which returns a hard coded value.

如果您正在测试的逻辑不关心数据库连接,那么您可以用返回硬编码值的存根替换该方法。

The mockito spy lets you check whether a method calls other methods. This can be very useful when trying to get legacy code under test.

mockito spy 让你检查一个方法是否调用了其他方法。这在尝试测试遗留代码时非常有用。

It is usful if you are testing a method that works through side effects, then you would use a mockito spy. This delegates calls to the real object and allows you to verify method invocation, number of times invoked etc.

如果您正在测试一种通过副作用起作用的方法,那么您将使用模拟间谍,这很有用。这将调用委托给真实对象,并允许您验证方法调用、调用次数等。

回答by Crazyjavahacking

Technically speaking both "mocks" and "spies" are a special kind of "test doubles".

从技术上讲,“模拟”和“间谍”都是一种特殊的“测试替身”。

Mockito is unfortunately making the distinction weird.

不幸的是,Mockito 使这种区别变得很奇怪。

A mock in mockito is a normal mockin other mocking frameworks (allows you to stub invocations; that is, return specific values out of method calls).

mockito 中的模拟是其他模拟框架中的普通模拟(允许您存根调用;也就是说,从方法调用中返回特定值)。

A spy in mockito is a partial mockin other mocking frameworks (part of the object will be mocked and part will use real method invocations).

mockito 中的 spy 是其他模拟框架中的部分模拟(部分对象将被模拟,部分将使用真正的方法调用)。

回答by Surasin Tancharoen

I have created a runable example here https://www.surasint.com/mockito-with-spy/

我在这里创建了一个可运行的示例https://www.surasint.com/mockito-with-spy/

I copy some of it here.

我在这里复制了一些。

If you have something like this code:

如果你有这样的代码:

public void transfer( DepositMoneyService depositMoneyService, 
                      WithdrawMoneyService withdrawMoneyService, 
                      double amount, String fromAccount, String toAccount) {
    withdrawMoneyService.withdraw(fromAccount,amount);
    depositMoneyService.deposit(toAccount,amount);
}

You may don't need spy because you can just mock DepositMoneyService and WithdrawMoneyService.

您可能不需要 spy,因为您可以模拟 DepositMoneyService 和 WithdrawMoneyService。

But with some legacy code, dependency is in the code like this:

但是对于一些遗留代码,依赖在代码中是这样的:

    public void transfer(String fromAccount, String toAccount, double amount) {
        this.depositeMoneyService = new DepositMoneyService();
        this.withdrawMoneyService = new WithdrawMoneyService();
        withdrawMoneyService.withdraw(fromAccount,amount);
        depositeMoneyService.deposit(toAccount,amount);
    }

Yes, you can change to the first code but then API is changed. If this method is being used by many places, you have to change all of them.

是的,您可以更改为第一个代码,但随后更改了 API。如果这个方法被很多地方使用,你必须改变所有的地方。

Alternative is that you can extract the dependency out like this:

另一种方法是您可以像这样提取依赖项:

    public void transfer(String fromAccount, String toAccount, double amount){
        this.depositeMoneyService = proxyDepositMoneyServiceCreator();
        this.withdrawMoneyService = proxyWithdrawMoneyServiceCreator();
        withdrawMoneyService.withdraw(fromAccount,amount);
        depositeMoneyService.deposit(toAccount,amount);
    }

    DepositMoneyService proxyDepositMoneyServiceCreator() {
        return new DepositMoneyService();
    }

    WithdrawMoneyService proxyWithdrawMoneyServiceCreator() {
        return new WithdrawMoneyService();
    }

Then you can use the spy the inject the dependency like this:

然后您可以使用 spy 注入依赖项,如下所示:

DepositMoneyService mockDepositMoneyService = mock(DepositMoneyService.class);
        WithdrawMoneyService mockWithdrawMoneyService = mock(WithdrawMoneyService.class);

    TransferMoneyService target = spy(new TransferMoneyService());

    doReturn(mockDepositMoneyService)
            .when(target)
            .proxyDepositMoneyServiceCreator();

    doReturn(mockWithdrawMoneyService)
            .when(target)
            .proxyWithdrawMoneyServiceCreator();

More detail in the link above.

在上面的链接中有更多详细信息。

回答by del bao

TL;DR version,

TL; DR 版本,

With mock, it creates a bare-bone shell instance for you.

使用mock,它会为您创建一个简单的 shell 实例。

List<String> mockList = Mockito.mock(ArrayList.class);

With spyyou can partially mock on an existinginstance

使用spy,您可以部分模拟现有实例

List<String> spyList = Mockito.spy(new ArrayList<String>());

Typical use case for Spy: the class has a parameterized constructor, you want to create the object first.

Spy 的典型用例:类有一个参数化的构造函数,你想先创建对象。

回答by Vu Truong

Both can be used to mock methods or fields. The difference is that in mock, you are creating a complete mock or fake object while in spy, there is the real object and you just spying or stubbing specific methods of it.

两者都可用于模拟方法或字段。不同之处在于,在模拟中,您正在创建一个完整的模拟或假对象,而在 spy 中,有真实的对象,而您只是在监视或存根它的特定方法。

While in spy objects, of course, since it is a real method, when you are not stubbing the method, then it will call the real method behavior. If you want to change and mock the method, then you need to stub it.

而在 spy 对象中,当然,因为它是一个真正的方法,当你不存根该方法时,它会调用真正的方法行为。如果你想改变和模拟方法,那么你需要存根它。

Consider the example below as a comparison.

考虑以下示例作为比较。

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;
?
import java.util.ArrayList;
import java.util.List;
?
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
?
@RunWith(MockitoJUnitRunner.class)
public class MockSpy {
?
????@Mock
????private List<String> mockList;
?
????@Spy
????private List<String> spyList = new ArrayList();
?
????@Test
????public void testMockList() {
????????//by default, calling the methods of mock object will do nothing
????????mockList.add("test");

        Mockito.verify(mockList).add("test");
        assertEquals(0, mockList.size());
????????assertNull(mockList.get(0));
????}
?
????@Test
????public void testSpyList() {
????????//spy object will call the real method when not stub
????????spyList.add("test");

        Mockito.verify(spyList).add("test");
        assertEquals(1, spyList.size());
????????assertEquals("test", spyList.get(0));
????}
?
????@Test
????public void testMockWithStub() {
????????//try stubbing a method
????????String expected = "Mock 100";
????????when(mockList.get(100)).thenReturn(expected);
?
????????assertEquals(expected, mockList.get(100));
????}
?
????@Test
????public void testSpyWithStub() {
????????//stubbing a spy method will result the same as the mock object
????????String expected = "Spy 100";
????????//take note of using doReturn instead of when
????????doReturn(expected).when(spyList).get(100);
?
????????assertEquals(expected, spyList.get(100));
????}
}

When shoud you use mock or spy? If you want to be safe and avoid calling external services and just want to test the logic inside of the unit, then use mock. If you want to call external service and perform calling of real dependency, or simply say, you want to run the program as it is and just stub specific methods, then use spy. So that's the difference between spy and mock in mockito.

什么时候应该使用模拟或间谍?如果你想安全,避免调用外部服务,只想测试单元内部的逻辑,那么使用模拟。如果您想调用外部服务并执行实际依赖项的调用,或者简单地说,您想按原样运行程序并仅存根特定的方法,则使用 spy。所以这就是 mockito 中 spy 和 mock 之间的区别。

回答by leo9r

I like the simplicity of this recommendation:

我喜欢这个建议的简单性:

  • If you want to be safe and avoid calling external services and just want to test the logic inside of the unit, then use mock.
  • If you want to call external service and perform calling of real dependencies, or simply say, you want to run the program as it is and just stub specific methods, then use spy.
  • 如果您想安全并避免调用外部服务而只想测试单元内部的逻辑,请使用mock
  • 如果您想调用外部服务并执行实际依赖项的调用,或者简单地说,您想按原样运行程序并仅存根特定方法,则使用spy

Source: https://javapointers.com/tutorial/difference-between-spy-and-mock-in-mockito/

来源:https: //javapointers.com/tutorial/difference-between-spy-and-mock-in-mockito/

A common difference is:

一个常见的区别是:

  • If you want to directly stub the method(s) of a dependency, then Mockthat dependency.
  • If you want to stub the data in a dependency so that all its methods return testing values that you need, then Spythat dependency.
  • 如果您想直接存根依赖项的方法,则Mock该依赖项。
  • 如果您想存根依赖项中的数据,以便其所有方法都返回您需要的测试值,则可以监视该依赖项。

回答by Vishwa Ratna

In short:

简而言之:

@Spyand @Mockare used heavily in testing of code, but developers do confuse in cases when to use one of them and thus developers end up in using @Mockto be safe.

@Spy并且@Mock在代码测试中被大量使用,但开发人员在何时使用其中之一的情况下确实会混淆,因此开发人员最终使用@Mock是安全的。

  • Use @Mockwhen you want to just test the functionality externallywithout actually calling that method.
  • Use @Spywhen you want to test the functionality externally + internallywith the very method being called.
  • 使用@Mock时,你只想测试功能,而不实际调用该方法。
  • @Spy当您想使用被调用的方法在外部 + 内部测试功能时使用。

Below is the example where i have taken the scenario of Election20xxin America.

下面是我在美国采用Election20xx场景的示例。

Voters can be divided according to VotersOfBelow21and VotersOfABove21.

选民可以根据VotersOfBelow21和进行划分VotersOfABove21

The ideal Exit poll says that Trump will win the election because VotersOfBelow21and VotersOfABove21both will vote for trump saying "We elected President Trump"

The ideal Exit poll says that Trump will win the election because VotersOfBelow21and VotersOfABove21both will vote for trump saying " We elected President Trump"

But this is not the real scenario:

但这不是真实的场景:

Voters of both age group voted for Trump because they had No other effective choice other than Mr Trump.

两个年龄段的选民都投票给特朗普,因为他们除了特朗普之外别无选择。

So how do you Test it??

那你怎么测试呢??

public class VotersOfAbove21 {
public void weElected(String myVote){
  System.out.println("Voters of above 21 has no Choice Than Thrump in 20XX ");
}
}


public class VotersOfBelow21 {
  public void weElected(String myVote){
    System.out.println("Voters of below 21 has no Choice Than Thrump in 20XX");
  }
}


public class ElectionOfYear20XX {
  VotersOfAbove21 votersOfAbove21;
  VotersOfBelow21 votersOfBelow21;
  public boolean weElected(String WeElectedTrump){
    votersOfAbove21.weElected(WeElectedTrump);
    System.out.println("We elected President Trump ");

    votersOfBelow21.weElected(WeElectedTrump);
    System.out.println("We elected President Trump ");
    return true;
  }

}


Now Note in above first two classes, both the age group people says that they do not have a better choice than trump. Which explicitly means that they voted for Trump just because they had no choice.

现在请注意,在上面的前两个类别中,这两个年龄段的人都说他们没有比特朗普更好的选择。这明确意味着他们投票给特朗普只是因为他们别无选择。

Now the ElectionOfYear20XXsays that Trump Won because both age group voted for him overwhelmingly.

现在ElectionOfYear20XX有人说特朗普赢了,因为两个年龄段的人都以压倒性优势投票给他。

If we were to Test the ElectionOfYear20XXwith @Mock, then we might not be able to get the real reason why Trump won, we will be just testing the external reason.

如果我们ElectionOfYear20XX用@Mock测试,那么我们可能无法得到特朗普获胜的真正原因,我们将只是测试外部原因。

If we test the ElectionOfYear20XXwith @Spy, then we get the real reason why Trump won with the external exit poll results, i.e internally + externally.

如果我们ElectionOfYear20XX使用@Spy 进行测试,那么我们就会得到特朗普凭借外部出口民意调查结果获胜的真正原因,即内部+外部。



Our ELectionOfYear20XX_Testclass:

我们ELectionOfYear20XX_Test班:

@RunWith(MockitoJUnitRunner.class)
public class ELectionOfYear20XX_Test {

  @Mock
  VotersOfBelow21 votersOfBelow21;
  @Mock
  VotersOfAbove21 votersOfAbove21;
  @InjectMocks
  ElectionOfYear20XX electionOfYear20XX;
  @Test
  public void testElectionResults(){
    Assert.assertEquals(true,electionOfYear20XX.weElected("No Choice"));
  }

}

This should outputjust the logic test results i.e. external check:

这应该只输出逻辑测试结果,即外部检查:

We elected President Trump 
We elected President Trump 


Testing with @Spyexternally as well as internally with actual method invocation.

@Spy使用实际方法调用在外部和内部进行测试。

@RunWith(MockitoJUnitRunner.class)
public class ELectionOfYear20XX_Test {

  @Spy
  VotersOfBelow21 votersOfBelow21;
  @Spy
  VotersOfAbove21 votersOfAbove21;
  @InjectMocks
  ElectionOfYear20XX electionOfYear20XX;
  @Test
  public void testElectionResults(){
    Assert.assertEquals(true,electionOfYear20XX.weElected("No Choice"));
  }

}

Output:

输出:

Voters of above 21 has no Choice Than Thrump in 20XX 
We elected President Trump 
Voters of below 21 has no Choice Than Thrump in 20XX
We elected President Trump