14

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

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

4

3 回答 3

21

Difference between mock and stub is very simple - mock can make your test fail, while stub can't. That's all there is. Additionally, you can think of stub as of something that provides values. Nowadays, fake is just a generic term for both of them (more on that later).

Example

Let's consider a case where you have to build a service that sends packages via communication protocol (exact details are irrelevant). You simply supply service with package code and it does the rest. Given the snippet below, can you identify which dependency would be a stub and which mock in potential unit test?

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

It's fairly easy to tell that packageBuilder simply provides value and there's no possible way it could make any test fail. That's a stub. Even though it might seem more blurry, packageService is stub too. It provides a value (what we do with the value is irrelevant from stub's point of view). Of course, later we'll use that value to test whether exception is thrown, but it's still all within our control (as in, we tell stub exactly what to do and forget about it - it should have no further influence on test).

It gets different with packageDistributor. Even if it provides any value, it's not consumed. Yet the call to Send seems to be pretty important part of our implementation and we'll most likely want to verify it is called.

At this point we should get to a conclusion that packageDistributor is a mock. We'll have a dedicated unit test asserting that Send method was called and if for some reasons it wasn't - we want to know that, as it's important part of the entire process. Other dependencies are stubs as all they do is provide values to other, perhaps more relevant pieces of code.

Quick glance at TDD

Stub being stub, could be just as well replaced with constant value in naive implementation:

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

This is essentially what mocking frameworks do with stubs - instruct them to return configurable/explicit value. Old-school, hand-rolled stubs often do just that - return constant value.

Obviously, such code doesn't make much sense, but anyone who ever done TDD surely seen bunch of such naive implementations at the early stage of class development. Iterative development that results from TDD will often help identify roles of your class' dependencies.

Stubs, mocks and fakes nowadays

At the beginning of this post I mentioned that fake is just a generic term. Given that mock can also serve as stub (especially when modern mocking frameworks are concerned), to avoid confusion it's good idea to call such object a fake. Nowadays, you can see this trend growing - original mock - stub distinction is slowly becoming a thing of the past and more universal names are used. For example:

  • FakeItEasy uses fake
  • NSubstitute uses substitute
  • Moq uses mock (name is old, but no visible distinction is made whether it's stub or mock)

References, further reading

于 2012-12-04T11:40:03.143 回答
6

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

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

  • Mock 比较复杂,常用于测试行为,例如验证函数是否被调用

于 2012-12-04T09:09:42.630 回答
2

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

  • 模拟/假对象将返回逼真的结果。
  • 存根将返回默认的失败/通过值。
于 2012-12-04T08:49:24.097 回答