9

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

例如,我有这段代码——

    [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);
    }

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

真诚感谢您的回答。

4

5 回答 5

18

Martin Fowler 在这里进行了很好的讨论。

从他的文章中:

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

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

在这些类型的替身中,只有模拟坚持行为验证。

于 2009-08-17T16:10:24.713 回答
3

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

模拟功能更强大,因为您还可以跟踪函数是否被调用、调用了多少次,甚至可以使用函数获得的值进行操作。

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

于 2009-08-17T14:23:29.387 回答
2

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

例如,我不会测试对 Hibernate 方法的调用,我假设 Hibernate 开发人员已经彻底测试过了。但是我测试我是否调用了正确的方法,使用一个模拟来建立这个期望。

于 2009-08-17T14:21:09.343 回答
0

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

于 2009-08-17T14:19:27.067 回答
0

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

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

于 2009-08-17T14:25:37.137 回答