使用Mockito时,嘲讽和间谍有什么区别?

Vic*_*azi 128 java testing mocking mockito

什么是使用Mockito间谍的用例?

在我看来,使用callRealMethod可以使用mock处理每个间谍用例.

我可以看到的一个区别是,如果你想让大多数方法调用都是真实的,它会节省一些代码行来使用模拟和间谍.这是它还是我错过了更大的图景?

JB *_*zet 93

答案在文档中:

真正的部分嘲笑(自1.8.0起)

最后,在邮件列表上进行了许多内部辩论和讨论之后,Mockito增加了部分模拟支持.以前我们认为部分嘲笑是代码味道.但是,我们找到了一个合理的部分模拟用例 - 更多阅读:这里

在发布之前,1.8 spy()并没有产生真正的部分嘲讽,而且对于一些用户来说这让人感到困惑.

callRealMethod()之后被介绍spy(),但间谍()当然留在那里,以确保向后兼容性.

否则,你是对的:间谍的所有方法都是真实的,除非被抄袭.除非callRealMethod()被调用,否则mock的所有方法都是stubed .一般来说,我更喜欢使用callRealMethod(),因为它不会强迫我使用doXxx().when()成语而不是传统when().thenXxx()

  • 在这些情况下,首选模拟而不是间谍的问题是,当类使用未注入其中(但在本地初始化)的成员时,该成员稍后会被“真实”方法使用;在模拟中,该成员将被初始化为其默认的 Java 值,这可能会导致错误的行为,甚至引发 NullPointerException。传递这个问题的方法是添加一个“init”方法,然后“真正”调用它,但这对我来说似乎有点过头了。 (2认同)
  • 文档中写道:“间谍应该偶尔谨慎使用,例如在处理遗留代码时。” 单元测试领域面临着做同一件事的方法太多的问题。 (2认同)

Sau*_*til 80

间谍和模拟之间的区别

当Mockito创建一个模拟时 - 它是从类的类中实现的,而不是从实际的实例中实现的.mock只是创建了一个Class的裸骨shell实例,完全用于跟踪与它的交互.另一方面,间谍将包装现有实例.它仍将以与普通实例相同的方式运行 - 唯一的区别是它还将被检测以跟踪与它的所有交互.

在下面的示例中 - 我们创建了一个ArrayList类的模拟:

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

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

    assertEquals(0, mockedList.size());
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的 - 在模拟列表中添加元素实际上并没有添加任何内容 - 它只是调用方法而没有其他副作用.另一方面,间谍的行为会有所不同 - 它实际上会调用add方法的真实实现并将元素添加到基础列表中:

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

    assertEquals(1, spyList.size());
}
Run Code Online (Sandbox Code Playgroud)

在这里,我们可以肯定地说,对象的真正内部方法被调用,因为当你调用size()方法时,你得到的大小为1,但是这个size()方法没有被模拟!那么1来自哪里?调用内部实际size()方法,因为size()没有被模拟(或存根),因此我们可以说该条目已添加到真实对象中.

来源:http://www.baeldung.com/mockito-spy +自我笔记.

  • 这个答案写得很好,很简单,帮助我最终理解了模拟和间谍之间的区别。好一个。 (3认同)

use*_*398 34

如果有一个包含8个方法的对象,并且您有一个测试,您想要调用7个实际方法并存根一个方法,那么您有两个选项:

  1. 使用模拟,你必须通过调用7 callRealMethod和stub一个方法来设置它
  2. 使用spy你必须通过存根一个方法来设置它

有关推荐使用间谍进行部分嘲笑的官方文档doCallRealMethod.

另请参阅javadoc spy(Object)以了解有关部分模拟的更多信息.Mockito.spy()是一种创建部分模拟的推荐方法.原因是它保证对正确构造的对象调用实际方法,因为你负责构造传递给spy()方法的对象.


Sur*_*oen 7

当您想为遗留代码创建单元测试时,Spy 会很有用。

我在这里创建了一个可运行的例子https://www.surasint.com/mockito-with-spy/,我在这里复制了一些。

如果你有这样的代码:

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

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

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

    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);
    }
Run Code Online (Sandbox Code Playgroud)

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

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

    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();
    }
Run Code Online (Sandbox Code Playgroud)

然后您可以使用 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();
Run Code Online (Sandbox Code Playgroud)

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


yoA*_*ex5 7

模拟与间谍

[测试双类型]

Mock是一个裸双重对象。该对象具有相同的方法签名,但实现为空并返回默认值 - 0 和 null

Spy是一个克隆的双精度对象。新对象是基于真实对象克隆的,但您可以模拟它

class A {
    String foo1() {
        foo2();
        return "RealString_1";
    }

    String foo2() {
        return "RealString_2";
    }

    void foo3() { foo4(); }

    void foo4() { }
}
Run Code Online (Sandbox Code Playgroud)
@Test
public void testMockA() {
    //given
    A mockA = Mockito.mock(A.class);
    Mockito.when(mockA.foo1()).thenReturn("MockedString");

    //when
    String result1 = mockA.foo1();
    String result2 = mockA.foo2();

    //then
    assertEquals("MockedString", result1);
    assertEquals(null, result2);

    //Case 2
    //when
    mockA.foo3();

    //then
    verify(mockA).foo3();
    verify(mockA, never()).foo4();
}

@Test
public void testSpyA() {
    //given
    A spyA = Mockito.spy(new A());

    Mockito.when(spyA.foo1()).thenReturn("MockedString");

    //when
    String result1 = spyA.foo1();
    String result2 = spyA.foo2();

    //then
    assertEquals("MockedString", result1);
    assertEquals("RealString_2", result2);

    //Case 2
    //when
    spyA.foo3();

    //then
    verify(spyA).foo3();
    verify(spyA).foo4();
}
Run Code Online (Sandbox Code Playgroud)