什么是模拟,什么时候应该使用它?

Posted

技术标签:

【中文标题】什么是模拟,什么时候应该使用它?【英文标题】:What is a mock and when should you use it? 【发布时间】:2010-09-17 20:44:37 【问题描述】:

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

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

【问题讨论】:

【参考方案1】:

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

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

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

http://martinfowler.com/articles/mocksArentStubs.html

【讨论】:

【参考方案2】:

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

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

【讨论】:

【参考方案3】:

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

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

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

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

【讨论】:

【参考方案4】:

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

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

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

模拟允许您保持测试相互独立且易于设置。

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

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

【讨论】:

【参考方案5】:

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

【讨论】:

以上是关于什么是模拟,什么时候应该使用它?的主要内容,如果未能解决你的问题,请参考以下文章

我啥时候应该嘲笑?

什么是 [Serializable],我应该什么时候使用它?

什么是空间索引,我应该什么时候使用它?

什么是递归,我应该什么时候使用它?

对于 Java 测试,我应该模拟客户端还是模拟服务器

什么是 MvcHtmlString,我应该什么时候使用它?