Test well and test often. by
dopey

There have been many posts and guides that help to clarify the difference between mocks, stubs, spies, fakes, doubles, etc. We’re going to try to do that one more time, but with a new approach:

Mocking, stubbing and spying are about the methods, NOT about the objects themselves.

It is confusing to call a test double a “mock” or a “stub”. A single instance could have a method mocked, another method stubbed, and yet another method be a spy. That object is always just a “test double”. The methods are mocked, stubbed or spied.

What are they?

These examples are written in ruby with RSpec, whose syntax should be understandable even if you haven’t seen it before.

In each example, I also annotate the three parts of a test:

  1. Arrange all necessary preconditions and inputs.
  2. Act on the object or method under test.
  3. Assert that the expected results have occurred.

Stubbing Methods

Here is an example of stubbing:

class DogWalker
  def walk(dog)
    dog.walk
  end
end

describe "DogWalker" do
  it "walks the dog" do
    # Arrange
    dog = double :dog
    walker = DogWalker.new

    # Stub :walk
    allow(dog).to receive(:walk).and_return(true)

    # Act
    result = walker.walk(dog)

    # Assert
    expect(result).to be_true
  end
end

Let’s just think of dog as a “test double”. We can then say that we have stubbed the walk method or that we have stubbed the response to the walk message.

This test follows the Arrange-Act-Assert ordering. It also doesn’t care whether or not we sent the walk message to dog. It only cares that walker.walk(dog) returned true.

Mocking Methods

Here is an example of mocking:

class DogWalker
  def walk(dog)
    dog.walk
  end
end

describe "DogWalker" do
  it "walks the dog" do
    # Arrange
    dog = double :dog
    walker = DogWalker.new

    # Assert
    # Mock :walk
    expect(dog).to receive(:walk)

    # Act
    walker.walk(dog)
  end
end

In this example, many would refer to dog as a mock. Instead, let’s think of dog as a “test double”, or just a “double”. Then, we can say that we have “mocked” the walk method.

An important thing to note here is that this test does not follow the typical Arrange-Act-Assert mode for testing. When mocking, you must set your expectation before you act. In other words, the Assertion comes before the Action. This may seem innocuous, but it can hide subtle problems. If one were to pull the expectation up into a before :each, then every spec would now have an extra assertion. Furthermore, it is surprising to see a test that ends with an action. Future developers may think you left off the assertion.

Spying on Methods

Finally, an example of spying:

class DogWalker
  def walk(dog)
    dog.walk
  end
end

describe "DogWalker" do
  it "walks the dog" do
    # Arrange
    dog = double :dog
    walker = DogWalker.new

    # Spy on :walk
    allow(dog).to receive(:walk)

    # Act
    result = walker.walk(dog)

    # Assert
    expect(dog).to have_received(:walk)
  end
end

When we set up a spy in RSpec, we first stub the method. RSpec does not differentiate between spies and stubs in the Arrange phase. This is very helpful because it means that we can share setup across multiple tests. Some tests may not care to assert that dog received the walk message, but others may. The stub may still be necessary for all tests, but, unlike with mocking, we have the ability to only assert when we want to.

Spying allows you to assert that a message was received without breaking the Arrange-Act-Assert ordering.

There are shortcuts to setting up spies in RSpec. You can do:

dog = double(:dog).as_null_object
# Or more recently (and equivalent):
dog = spy(:dog)

By doing this, you no longer need the allow, which is quite convenient. The double will record every message sent to it. Because of this, we can break from the main argument of this post and refer to double as a spy.

When do I use them?

Methods should fall into one of the two categories: query or command. Martin Fowler gives these definitions:

  • Queries: Return a result and do not change the observable state of the system (are free of side effects).
  • Commands: Change the state of a system but do not return a value.

Stub if your system under test is calling another object’s query method. Your system under test will typically do something with that result and you can test what it does with the result. You do not actually care if that query method is called as long as the system under test ultimately does what it should.

Spy if you want to ensure that a message was a received by your test double. This is necessary when you are calling a command method.

Do not bother mocking. Mocking can save you a line of code (unless you use spy or as_null_object), but it breaks the Arrange-Act-Assert test structure. Ultimately, they do the same thing, but spying is more flexible and easier to reason about.

For further discussion on Queries vs Command and when to test in which way, I would suggest that you watch Sandi Metz’s talk: Magic Tricks of Testing. It is quite good.