30

我刚刚阅读了关于模拟对象的维基百科文章,但我仍然不完全清楚它们的目的。当实际对象过于复杂或不可预测时,它们似乎是由测试框架创建的对象(您知道 100% 确定模拟对象的值是什么,因为您完全控制它们)。

但是,我的印象是所有测试都是使用已知值的对象完成的,所以我一定遗漏了一些东西。例如,在一个课程项目中,我们的任务是使用日历应用程序。我们的测试套件由我们确切知道它们是什么的事件对象组成,因此我们可以测试多个事件对象、各种子系统和用户界面之间的交互。我猜这些是模拟对象,但我不知道你为什么不这样做,因为没有已知值的对象,你就无法测试系统。

4

5 回答 5

32

模拟对象不仅仅是具有已知值的对象。它是一个对象,其接口与您不能在测试中使用的复杂对象(如数据库连接和结果集)具有相同的接口,但具有您可以在测试中控制的实现。

有一些模拟框架允许您即时创建这些对象,并且实质上允许您说以下内容:使用 foo 方法让我成为一个对象,该方法接受一个 int 并返回一个 bool。当我通过 0 时,它应该返回 true。然后您可以测试使用 foo() 的代码,以确保它做出适当的反应。

Martin Fowler 有一篇关于模拟的精彩文章:

于 2008-10-17T22:56:06.533 回答
10

想想拥有客户端和服务器软件的经典案例。测试客户端,需要服务器;要测试服务器,您需要客户端。这使得单元测试几乎不可能 - 不使用模拟。如果你模拟服务器,你可以单独测试客户端,反之亦然。

模拟的重点不是复制它模拟的事物的行为。更多的是充当一个简单的状态机,其状态变化可以通过测试框架进行分析。因此,客户端模拟可能会生成测试数据,将其发送到服务器,然后分析响应。您期望对特定请求的特定响应,因此您可以测试是否得到它。

于 2008-10-17T22:55:43.040 回答
6

我同意@Lou Franco所说的一切,你绝对应该阅读@Lou Franco 指出的关于测试双打的优秀 Martin Fowler 文章。

任何测试替身(fake、stub 或 mock)的主要目的是隔离被测对象,以便您的单元测试仅测试该对象(而不是它的依赖项以及与之协作或交互的其他类型)。

可以使用提供对象所依赖的接口的对象来代替实际依赖项,以便可以预期某些交互将发生。这可能很有用,但围绕基于状态的测试与基于交互的测试存在一些争议。过度使用模拟期望会导致脆弱的测试。

测试替身的另一个原因是消除对数据库或文件系统或其他类型的依赖,这些类型的设置或执行耗时的操作非常昂贵。这意味着您可以将对感兴趣的对象进行单元测试所需的时间降至最低。

于 2008-10-17T23:12:22.303 回答
2

这是一个示例:如果您正在编写填充数据库的代码,您可能需要检查特定方法是否已将数据添加到数据库中。

设置数据库副本进行测试存在的问题是,如果假设在调用被测方法之前没有记录,之后有一条记录,那么您需要将数据库回滚到以前的状态,从而增加了开销用于运行测试。

如果您假设只比以前多一条记录,它可能会与连接到同一数据库的第二个测试人员(甚至是同一代码中的第二个测试)发生冲突,从而导致依赖关系并使测试变得脆弱。

模拟允许您保持测试彼此独立并且易于设置。

这只是一个例子——我相信其他人可以提供更多。

我 100% 同意该主题的其他贡献者,尤其是对 Martin Fowler 文章的推荐。

于 2008-12-04T11:50:53.597 回答
0

您可能对我们的书感兴趣,请参阅http://www.growth-object-orientated-software.com/。它在 Java 中,但这些想法仍然适用。

于 2009-05-21T16:45:36.523 回答