【问题标题】:What is the difference between mocks, stubs and fake objects [closed]模拟,存根和假对象之间有什么区别[关闭]
【发布时间】:2012-11-21 20:19:10
【问题描述】:

尽管有很多资源,即使在 SO 上,在这些 Q/A 中也只有两个术语相互比较。

那么,简而言之,它们各自是什么?它们又是如何相互关联的?还是根本没有?

【问题讨论】:

标签: unit-testing mocking stub


【解决方案1】:

mock 和 stub 之间的区别非常简单 - mock 可以使您的测试失败,而 stub 不能。这就是全部。此外,您可以将存根视为提供值的东西。如今,fake 只是两者的总称(稍后会详细介绍)。

示例

让我们考虑一个案例,您必须构建一个通过通信协议发送包的服务(具体细节无关紧要)。您只需提供带有包代码的服务,剩下的就交给它了。鉴于下面的 sn-p,您能否确定哪个依赖项是 stub 以及潜在单元测试中的哪个 mock?

public class DistributionService
{
    public double SendPackage(string packageCode)
    {
        var contents = this.packageService.GetPackageContents(packageCode);
        if (contents == null)
        {
            throw new InvalidOperationException(
                "Attempt to send non-exisiting package");
        }

        var package = this.packageBuilder.Build(contents);
        this.packageDistributor.Send(package);
    }
}

很容易看出packageBuilder 只是提供价值,它不可能使任何测试失败。这是一个存根。即使看起来更模糊,packageService 也是存根。它提供了一个值(从存根的角度来看,我们对这个值所做的事情是无关紧要的)。当然,稍后我们将使用该值来测试是否抛出异常,但它仍然在我们的控制范围内(例如,我们准确地告诉 stub 做什么而忘记它——它应该对测试没有进一步的影响)。

packageDistributor 会有所不同。即使它提供任何价值,也不会被消耗。然而,对Send 的调用似乎是我们实现中非常重要的一部分,我们很可能希望验证它是否被调用。

此时我们应该得出结论,packageDistributor 是一个mock。我们将有一个专门的单元测试断言 Send 方法被调用,如果由于某些原因它没有被调用 - 我们想知道这一点,因为它是整个过程的重要部分。其他依赖项都是存根,因为它们所做的只是为其他可能更相关的代码片段提供值。

快速浏览 TDD

存根是存根,在朴素实现中也可以用常量值替换:

var contents = "Important package";
var package = "<package>Important package</package>";
this.packageDistributor.Send(package);

这本质上是模拟框架对存根所做的 - 指示它们返回可配置/显式值。 老式的、手动的存根经常这样做 - 返回常量值。

显然,这样的代码没有多大意义,但任何做过 TDD 的人肯定在类开发的早期阶段看到过很多这样的幼稚实现。 TDD 产生的迭代开发通常有助于识别角色类的依赖关系。

如今的存根、模拟和赝品

在本文开头我提到 fake 只是一个通用术语。鉴于 mock 也可以用作存根(尤其是在涉及现代模拟框架时),为避免混淆,将此类对象称为假对象是个好主意。如今,您可以看到这种趋势正在增长 - 原始 mock - stub 区别正在慢慢成为过去,并且使用了更通用的名称。例如:

  • FakeItEasy 使用
  • NSubstitute 使用 substitute
  • Moq 使用 mock(名称很旧,但无论是 stub 还是 mock 都没有明显的区别)

参考资料,进一步阅读

【讨论】:

  • 感谢您的详尽回答。我特别喜欢第一句话,它把它带到了重点,你提到现在这一切都有点模糊成“假”了。
【解决方案2】:

Mock 和 Stub 都称为 Fake Object。在我看来:

  • Stub 用于替换外部依赖,它使我们的测试无异常运行。我们必须使用 Assert 来确定测试是否失败。 Stub只适用于测试某些函数的结果是否正确

  • Mock 更复杂,通常用于测试行为,例如验证是否调用了函数

【讨论】:

    【解决方案3】:

    它们通常可以互换,但在我看来略有不同。

    • 模拟/伪造对象将返回逼真的结果。
    • 存根将返回默认的失败/通过值。

    【讨论】:

      猜你喜欢
      • 2011-03-28
      • 2015-09-08
      • 1970-01-01
      • 2011-11-08
      • 2010-09-18
      • 2014-10-02
      • 1970-01-01
      • 2014-10-19
      相关资源
      最近更新 更多