什么时候使用存根和模拟?

时间:2009-08-17 14:13:23

标签: unit-testing mocking stubbing

我一直都有这种困惑。如果我编写一个使用伪代码来断言某些操作的代码,那么当它真正使用真实对象而不是假对象时,我如何相信我的实际实现。

例如,我有这段代码 -

    [Test]
    public void CanCreateContactsWithData()
    {
        using(ISession session = factory.OpenSession())
        using (ITransaction trans = session.BeginTransaction())
        {
            _contactId = (long) session.Save(contact);
            trans.Commit();
        }

        Assert.AreNotEqual(0, _contactId);
    }

此代码测试“联系”对象的实现,无论是否将其保存到数据库中。如果我碰巧使用存根而不是真正的数据库连接,我是否需要单独测试将其存储在数据库中?而且,你们称之为集成测试吗?

回答非常感谢。

5 个答案:

答案 0 :(得分:14)

Martin Fowler有一个很好的讨论here

从他的文章:

Meszaros使用术语Test Double作为任何类型的假装对象的通用术语,用于代替真实对象进行测试。这个名字来自电影中特技双人的概念。 (他的目标之一是避免使用已广泛使用的任何名称。)Meszaros然后定义了四种特殊的双重类型:

  • 虚拟对象传递但从未实际使用过。通常它们只用于填充参数列表。
  • 虚假对象实际上有工作实现,但通常需要一些使它们不适合生产的快捷方式(内存数据库就是一个很好的例子)。
  • Stubs为测试期间的调用提供了固定的答案,通常根本不会对测试中编程的任何内容做出任何响应。存根还可以记录有关呼叫的信息,例如记住它'发送'的消息的电子邮件网关存根,或者可能只记录它'发送'的消息。
  • 模拟是我们在这里讨论的:预先编程了预期的对象,形成了预期接收的呼叫规范。

在这些双打中,只有嘲讽坚持行为验证。

答案 1 :(得分:2)

您应该测试您编写的代码。如果您编写了数据库连接对象代码,那么请对其进行测试。否则,如果它是具有自己的测试的库的一部分,你可以模拟/存根它并假设如果连接对象通过它自己的测试套件,那么它可以工作。

例如,我不会测试对Hibernate方法的调用,我认为Hibernate开发人员已经对它进行了彻底的测试。但我测试我正在调用正确的方法,使用模拟来设置期望值。

答案 2 :(得分:2)

当您只想要一个函数返回某个值(或什么都不做)时,您可以使用存根。你真的不关心函数是否被调用,你只想隔离事物。

模拟更强大,因为你可以跟踪函数是否被调用,多少次,甚至用你的函数获得的值来做事。

在您的情况下,如果您想模拟数据库(因此它变成单元测试而不是功能测试),您可以模拟ISession和ITransaction。然后,您可以将此值存储在内存中,并检查是否保存了正确的值。

答案 3 :(得分:0)

是的,根据您的定义,使用真实数据库将更具功能性或集成测试。就个人而言,我觉得单元测试应该只测试那种方法,而不是其他所有方法。因此,无论会话或事务是否有效,您的单元测试必须确保 这些对象在必要时被调用以进行工作 - 这就是模拟和存根进入的地方。使用它们来确保您的单元测试与外部功能分离,以便可以作为基本单元进行测试;理想情况下无论如何。

答案 4 :(得分:0)

大多数单元测试都是关于测试单个代码片段,而存根和模拟只是帮助您逐个测试的工具。通过单独测试各个部分,您可以更详细地测试每个部分,但不能保证完整图片的任何内容。各种集成测试都可以做到这一点。

在测试更大的功能时,我经常发现数据库逻辑的实际集成测试是最不重要的测试工件,因为您经常会在UI级别测试这些相同的操作。