模拟框架中的模拟与间谍活动

Posted

技术标签:

【中文标题】模拟框架中的模拟与间谍活动【英文标题】:Mocking vs. Spying in mocking frameworks 【发布时间】:2012-10-01 09:51:06 【问题描述】:

在模拟框架中,您可以模拟一个对象或窥探它。两者有什么区别,我什么时候应该/应该使用其中一个?

以 Mockito 为例,我看到使用 spiesmocks 可以做类似的事情,但我不确定两者之间的区别。

【问题讨论】:

martinfowler.com/bliki/TestDouble.html ***.com/questions/28295625/mockito-spy-vs-mock的可能重复 @Abhinav 这个问题比那个问题早。 【参考方案1】:

基于 Martin Fowler 的 Mocks Aren't Stubs:

Dummy 对象被传递但从未实际使用过。通常它们只是用来填充参数列表。

Fake 对象实际上有工作实现,但通常采取一些捷径,这使得它们不适合生产(内存数据库就是一个很好的例子)。

存根为测试期间拨打的电话提供预设答案,通常根本不响应任何超出测试程序的内容。

Spies 是存根,它还根据调用方式记录一些信息。其中一种形式可能是电子邮件服务,它记录发送了多少消息。

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

【讨论】:

【参考方案2】:

我将尝试在这里用一个例子来解释:

// 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,我们在其中添加了一个元素并且预期大小为一。

我们窥探真实的对象意味着我们可以指示哪个方法被存根。所以我们声明我们在 spy object 上存根方法 - size(),无论实际大小是多少,它都会返回 10。

简而言之,您将spy 真实对象存根 一些方法

【讨论】:

【参考方案3】:

Mockito 警告说,部分模拟不是一个好的做法,您应该修改您的面向对象架构。 Spy(或部分模拟)建议用于测试遗留代码

【讨论】:

【参考方案4】:

在 Mockito 中,如果您将任何对象分配给 Mock Object 的实例变量,则不会影响 Mock Object。

但是在 Spy 的情况下,如果您将任何对象分配给 Spy Object 的实例变量,那么确实会影响 Spy Object,因为 Spy 的行为类似于实时对象修改。

供参考的例子有

@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));
    

【讨论】:

【参考方案5】:

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

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

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

【讨论】:

【参考方案6】:

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

【讨论】:

【参考方案7】:

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

进行间谍活动时,您会获取现有对象并仅“替换”某些方法。当您有一个庞大的类并且只想模拟某些方法(部分模拟)时,这很有用。让我引用Mockito documentation:

您可以创建真实对象的间谍。当您使用 spy 时,会调用 真正的 方法(除非方法被存根)。

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

如有疑问,请使用模拟。

【讨论】:

谢谢!这使它更加清晰。所以模拟永远不会委托给被模拟的实际对象永远,但间谍会。 模拟没有“实际对象”——模拟是从头开始创建的。 任何解释为什么 Mockito 一直警告不要使用间谍?我看到他们说喜欢模拟,但我不清楚原因。 我不确定,但可能是因为它们是“Mockito”而不是“Spyito”:D 我的猜测是他们更喜欢模拟而不是间谍,因为你想在单元测试时尽可能地限制被测代码。通过使用 spy,您允许一些真正的实现代码运行,允许该依赖类的代码包含在您的测试中。

以上是关于模拟框架中的模拟与间谍活动的主要内容,如果未能解决你的问题,请参考以下文章

使用 Mockito 时,模拟和间谍有啥区别?

角色与间谍的间谍

Mockito:将InOrder与间谍对象一起使用

xml Spock模拟存根间谍

匹配器错误:接收到的值必须是模拟或间谍函数

Android:Mockito(2.0.2-beta) - 无法模拟/间谍最后的课程