What is the difference between mocks, stubs and fa

2020-05-26 07:20发布

问题:

Although there are plenty of resources, even here on SO, only two of the terms are compared to each other in these Q/A.

So, in short, what is each one of them? And how they all relate to each other? Or don't they at all?

回答1:

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

  • Mocks aren't stubs by Martin Fowler - you can see this article being linked virtually under any stub/mock question, and it's for a reason
  • Exploring The Continuum Of Test Doubles by Mark Seemann - overview for all the confusing unit testing terminology (if you thought mock, fake and stub are enough, you probably should know there's also *dummy**, spy, double and what not)
  • xunitpatterns.com - backing-website for huge unit-testing-patterns book, xUnit Test Patterns: Refactoring Test Code by Gerard Meszaros
  • Art of Unit Testing by Roy Osherove - excellent unit testing introductory book (the "mock can make test fail, stub not" are Roy's words, not mine)


回答2:

Mock and Stub are both called Fake Object. In my opinion:

  • Stub is used to replace the external dependency, it make our tests run without exceptions. We must use Assert to determine the test fail or not. Stub is only suitable for testing the result of some functions are correct or not

  • Mock is more complex, often used to test behaviors, e.g. verifying is a function called or not



回答3:

They are generally interchangeable but there are slight differences in my opinion.

  • A mock/fake object will return realistic looking results.
  • A stub will return a default fail/pass value.