使用Mockito间谍会有什么用例?
在我看来,使用callRealMethod可以使用mock处理每个间谍用例。
我可以看到的一个区别是,如果你想让大多数方法调用都是真实的,它会节省一些代码行来使用模拟和间谍。这是它还是我错过了更大的图景?
答案 0 :(得分:86)
在真正的部分嘲笑(自1.8.0起)
最后,在经过多次内部辩论后,在邮件列表上的讨论中,Mockito增加了部分模拟支持。以前我们认为部分嘲笑是代码味道。但是,我们发现了一个合理的部分模拟用例 - 更多阅读:here
在发布之前,1.8 spy()并没有产生真正的部分嘲讽,而且对于一些用户来说这让人感到困惑。
callRealMethod()
之后引入了{p> spy()
,但当然留下了spy(),以确保向后兼容。
否则,你是对的:间谍的所有方法都是真实的,除非存根。除非调用callRealMethod()
,否则模拟的所有方法都是存根的。一般来说,我更喜欢使用callRealMethod()
,因为它并不强迫我使用doXxx().when()
成语而不是传统的when().thenXxx()
答案 1 :(得分:69)
间谍与模拟之间的区别
当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());
}
正如您所看到的 - 在模拟列表中添加元素实际上并没有添加任何内容 - 它只是调用方法而没有其他副作用。另一方面,间谍的行为会有所不同 - 它实际上会调用add方法的真实实现并将元素添加到基础列表中:
@Test
public void whenCreateSpy_thenCreate() {
List spyList = Mockito.spy(new ArrayList());
spyList.add("one");
Mockito.verify(spyList).add("one");
assertEquals(1, spyList.size());
}
这里我们可以肯定地说,调用了对象的真正内部方法,因为当你调用size()方法时,你得到的大小为1,但是这个size()方法没有被模拟! 那么1来自哪里?内部实际size()方法被调用,因为size()没有被模拟(或存根),因此我们可以说该条目被添加到真实对象中。
来源:http://www.baeldung.com/mockito-spy +自我注释。
答案 2 :(得分:32)
如果有一个包含8个方法的对象,并且您有一个测试,您想要调用7个实际方法并存根一个方法,则有两个选项:
spy
,您必须通过存根一种方法进行设置 doCallRealMethod
{{1}}建议使用间谍进行部分嘲笑。
另请参阅javadoc spy(Object)以了解有关部分模拟的更多信息。 Mockito.spy()是一种创建部分模拟的推荐方法。该 原因是它保证真正的方法被正确地调用 构造对象,因为你负责构造 对象传递给spy()方法。
答案 3 :(得分:4)
Mock
与Spy
Mock
是一个简单的双重对象。该对象具有相同的方法签名,但实现为空,并返回默认值-0和null
Spy
是一个克隆的double对象。新对象是根据 real 对象克隆的,但是您可以对其进行模拟
class A {
String foo1() {
foo2();
return "RealString_1";
}
String foo2() {
return "RealString_2";
}
void foo3() { foo4(); }
void foo4() { }
}
@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();
}
答案 4 :(得分:3)
当你想为遗留代码创建单元测试时,间谍非常有用。
我在这里创建了一个可运行的示例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);
}
您可能不需要间谍,因为您只能模拟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);
}
是的,您可以更改为第一个代码,但随后会更改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();
}
然后你就可以像这样使用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();
上面链接中的更多细节。