Mocking 与 Mocking 框架中的间谍

在 Mocking 框架中,可以对一个对象进行 嘲笑或者对其进行 间谍。这两者之间的区别是什么? 我应该在什么时候使用它们?

莫基托为例,我看到使用 间谍嘲笑做了类似的事情,但是我不确定这两者之间的区别。

80098 次浏览

模拟对象完全替换模拟类,返回记录或默认值。你可以凭空创建模拟。这是在单元测试期间最常用的方法。

在间谍活动中,您使用一个现有的对象,并且只“替换”一些方法。当您有一个庞大的类并且只想模仿某些方法(部分模仿)时,这非常有用。让我引用 Mockito 文档的话:

你可以创造真实物体的间谍。当您使用间谍时,就会调用 真的方法(除非某个方法被截断)。

真正的间谍应该使用 小心地,偶尔地,例如在处理遗留代码时。

有疑问时,使用模拟。

Mockito 警告说,部分模仿不是一个好的实践,您应该修改面向对象的体系结构。建议使用 间谍(或部分模拟)来测试 遗留代码

间谍有两个定义。一个是调用实际方法的地方,另一个是,没有调用任何功能,只返回 null 或 null 等价值,但是调用了方法,并且记录了它们的状态,通常类似于,方法 x 被调用 y 次。

我将试着用一个例子来解释:

// Difference between mocking, stubbing and spying
@Test
public void differenceBetweenMockingSpyingAndStubbing() {
List list = new ArrayList();
list.add("abc");
assertEquals(1, list.size());


List mockedList = spy(list);
when(mockedList.size()).thenReturn(10);
assertEquals(10, mockedList.size());
}

在这里,我们有初始的实际对象 list,其中我们添加了一个元素,并期望大小为1。

我们 间谍实物意味着我们可以指示哪个方法是 被撞死了。因此,我们声明,我们在 间谍物品上的方法 -size()将返回10,不管实际大小是多少。

简而言之,你将 间谍 真实的物体票根 一些方法

参考资料: http://javapointers.com/tutorial/difference-between-spy-and-mock-in-mockito/

使用模拟对象时,当不存根时方法的默认行为是什么也不做。简单的意思是,如果它是一个 void 方法,那么当你调用这个方法时它什么也不做,或者如果它是一个带有返回值的方法,那么它可能返回 null、 null 或默认值。

当然,在间谍对象中,由于它是一个真正的方法,当您不使用该方法存根时,它将调用真正的方法行为。如果您想要更改并模拟该方法,那么您需要将其存根。

在 Mockito,如果你为模拟对象赋予任何对象实例变量,那么它不会影响模拟对象。

但是在间谍的情况下,如果你分配任何对象给间谍对象的实例变量,那么就会对间谍对象产生影响,因为间谍就像实时对象修改一样。

参考示例是

@RunWith(MockitoJUnitRunner.class)
public class MockSpyExampleTest {


@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");
assertNull(mockList.get(0));
}


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

基于 嘲笑不是存根 by Martin Fowler:

虚拟的 对象被传递,但从来没有实际使用过。通常它们只是用来填充参数列表。

对象实际上具有可工作的实现,但通常采用一些不适合生产的快捷方式(内存中的数据库就是一个很好的例子)。

存根 为测试期间打出的电话提供预先录制好的答案,通常对测试程序之外的任何东西都不做任何反应。

间谍 是一种存根,它也根据被称呼的方式记录一些信息。其中一种形式可能是电子邮件服务,记录发送了多少邮件。

Mocks 就是我们在这里讨论的对象: 预先编写了期望的对象,这些对象形成了它们期望接收的调用的规范。

如果我们想避免调用外部服务,只是想测试方法内部的逻辑,那么就使用 mock。

如果我们想要调用外部服务并使用真正的依赖关系,即按原样运行程序,只使用特定的存根方法,那么就使用 span