Sinon Spies vs. Stubs

Sinon provides spies, stubs, and mocks. They're all useful as fakes in tests. They come with essential differences for what they're helpful in doing and how they work.

Why Use Fakes?

In a unit test, you might want to avoid having to test the unit's dependencies. This is especially true in white-box testing. In this case, test fakes are going to be very helpful. Sinon provides several fakes, notably spies, stubs, and mocks. Let's compare and contrast the three:

Sinon Spies

Spies sound like what they do -- they watch your functions and report back on how they are called. They generally avoid the violence and mayhem of a Hollywood spy, but depending on your application, this could vary.

They don't change the functionality of your application. They simply report what they see. The sinon API for spies is fairly large, but it essentially centers around the called attribute (of which there are many variations).

I first setup that I want to spy on something. Then I call my subject under test (src code). Then I verify with the spy what was actually called and stop spying. That might look like this in a test:

describe("#fight", () => {
  it("calls prayForStrength for fight success", () => {
    sinon.spy(subject.strengthDep, "prayForStrength")
    subject.fight()
    subject.strengthDep.called.should.be.true
    subject.strengthDep.restore()
  });
});

Note: this example is in mocha using a should.js assertion style

The dependency's prayForStrength method is referred to by name in a string to setup the spy. When fight is called here, strengthDep.prayForStrength will be called as normal -- but there will be someone watching. Finally, we call restore on the function we spied on so that all spies are called off. If you want to do more than watch as dependencies work as described, you might want to use a stub.

Sinon Stubs

Stubs are more hands-on than spies (though they sound more useless, don't they). With a stub, you will actually change how functions are called in your test. You don't want to change the subject under test, thus changing the accuracy of your test. But you may want to test several ways that dependencies of your unit could be expected to act.

For instance, if you had a function that returned a boolean that your code used to do different things, you might want to use a stub in two different tests to verify conditions when returning different values (ie, guarantee one run of true and one of false return).

To continue the fight example from above, let's assume that if prayForStrength returns true, we are guaranteed to win the fight for the orphans (ie, fight() should return true). That might look like this:

describe("#fight", () => {
  it("always wins when prayForStrength is true", () => {
    sinon.stub(subject.strengthDep, "prayForStrength").callsFake(() => true)
    subject.fight().should.be.true
    subject.strengthDep.restore()
  });
});

Notice that we use a different sinon.stub API. This call chains to an invocation of callsFake where we're supplying our own version of prayForStrength. For our test, all we care about is the return value, so that's all we supply. We're not testing this dependency. We're instead testing how our subject fights in a certain circumstance. There are many ways you can use sinon stubs to control how functions are called. Also note that you can still use the called verifications with stubs. But if you do verify a stub was called, you may want to use a mock.

Sinon Mocks

Mocks take the attributes of spies and stubs, smashes them together and changes the style a bit. A mock will both observe the calling of functions and verify that they were called in some specific way. And all this setup happens previous to calling your subject under test. After the call, mocks are simply asked if all went to plan.

So the previous test could be rewritten to use a mock:

describe("#fight", () => {
  it("always wins when prayForStrength is true", () => {
    var mock = sinon.mock(subject.strengthDep)
    mock.expects("prayForStrength").returns(true)
    subject.fight().should.be.true
    mock.verify()
    mock.restore()
  });
});

The expects and returns line is where the combo magic happens. expects is verifying a call (like spies can), and returns is specifying functionality (like stubs can). The verify call is the line that will fail (essentially the mock assertion) if things in the subject didn't go exactly according to plan.

Spies vs. Stubs vs. Mocks

So when should I use spies or stubs or mocks? As with most art, there are many ways to accomplish what you want. Much of your choice will depend on your own style and what you become proficient in.

Some basic rules might be:

  • Use Spies - if you simply want to watch and verify somethings happens in your test case.

  • Use Stubs - if you simply want to specify how something will work to help your test case.

  • Use Mocks - if you want to both of the above on a single dependency in your test case.

When do you find yourself most often using spies vs. stubs vs. mocks?