System under test
Let’s assume we have a situation similar to the one described by Uncle Bob Martin in “The Little Mocker”1.
We have a
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
that delegates the process of logging users in to the
Authoriser, which implementation might look more or less like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
How can we prove that a newly created
System has no logged in users?
And do we even need to construct a real
Authoriser object for that?
Since we know that calling
system.loginCount does not even touch the
we can replace the
authoriser with a dummy:
1 2 3 4 5 6 7 8 9 10
And that’s all there is to a dummy! You pass it into something when you have to provide an argument, but you know that it will never be used.
Let’s now suppose that you want to test a part of your
System that requires you to be logged in.
Of course you might say that you could just log in, but you’ve already tested that login works, and since the process of logging in takes time, why do it twice? Also, if there’s a bug in login your test would fail too! That’s an unnecessary coupling that can be easily avoided with a stub:
1 2 3 4 5 6 7 8 9 10 11 12
So what does this stubbed
accepting_authoriser do? It returns true, therefore accepting any username/password pairs.
If you now wanted to test another part of your system that handles unauthorised users you could create a
rejecting_authoriser like this:
So that’s a stub, it simply returns a value and has no other logic in it.
Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what’s programmed in for the test.
Beautiful! So nice and simple, isn’t it? Just one line of code! Isn’t duck typing brilliant?
Imagine how much more code you’d have to write if you were using Java or C#!
You’d probably have to create an
Authoriser interface in the first place,
then make sure both the real implementation and your stub
interface, then …
Oh yeah, the
The trouble with using plain-old-js for hand-rolling your test stubs is that you won’t get a meaningful error when the interface of the stubbed-out object changes and your test stubs are no longer correct. In worst case scenario you might not even get an error at all!
What could be the consequences of this, you may ask? Well, I’ve seen huge test suites pass even though the application they were supposed to test was fundamentally broken as half the code didn’t even exist anymore… Trust me, you don’t want to be there2 :–)
Right, so this situation is not cool, but can we do better than that? Yes we can! Enter sinon.js3!
How would the
accepting_authoriser look implemented with sinon then?
1 2 3 4 5 6 7 8 9 10 11 12 13
And what makes it different? Calling
sinon.createStubInstance(Authoriser) creates a stub object
that allows you to stub out only those methods that exist on the
prototype of the original
Now that’s important because it means that should the
#authorise method happily decide to change its
name to say
#isAuthorised one day, you’d get a
TypeError in your test,
exactly where you attempt to stub out the no longer existing
#authorise. Cool, right?
Do you remember when I said in the beginning that
delegates the process of logging users in to the
How can we test whether or not this interaction really takes place?
What we’d need to do is spy on the caller to see inside the workings of the algorithm we’re testing –
and that’s precisely what spies are for4:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
You inject a spy object in exactly the same way you’d inject a stub, but then at the end of your test you check if the interactions recorded by your spy are the ones you expected it to see.
Spies are stubs that also record some information based on how they were called.
Another thing to note is that
because a spy maintains state (
authorise_was_called flag in this example), it needs to be reset between the tests
to avoid one test case affecting another – this is done in the
Of course our hand-rolled implementation suffers from the problems I described earlier when we talked about hand-rolling stubs. Let’s improve our previous implementation with sinon:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Similarly to the stub example, I’m also using
There’s one significant difference between our hand-rolled spy implementation and the one above though:
sinon spy itself is not the main object you inject, it’s a wrapper around object’s method.
That’s why I inject the
accepting_authoriser but perform the assertion on
accepting_authoriser.authorise spy and
accepting_authoriser.authorise.restore() to reset it.
Additionally, the fact that sinon spy is a wrapper means that if you wanted to spy
on the real
Authoriser's method you could do it like so:
1 2 3 4 5 6 7
A mock is not so interested in the return values of functions.
It’s more interested in what function were called, with what arguments, when, and how often.
Another thing that makes a mock5 different from a stub is its
which groups all the assertions and performs them at the end of a test, verifying our
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
Now that we understand how mocks work and we know how to build them ourselves, let’s see how sinon can make things easier. There’s an essential distinction we need to make before diving into sinon mocks though:
In Sinon, the basic unit of faking is functions, always functions.
… and same goes for sinon mocks. OK, but what does this mean to you? Let’s have a look at differences in the implementation first:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
An important thing to note here is that sinon mock is created as a wrapper around
an instance of the real
Authoriser, but instead of the mock we’re injecting the already mentioned real instance
This strategy might be problematic if instantiation of your depended-on object (
Authoriser in our example)
is expensive; If that’s the case you might want to avoid calling the constructor directly by using
Note: At the time of writing sinon.js (version 1.10.3) does not provide any equivalent of
Fake has business behavior. You can drive a fake to behave in different ways by giving it different data.
Suppose we wanted to define several personas to whom our
System responded differently.
Let’s say “bob” knows his password and “alice” forgot it; My personal recommendation
would be to use two separate stubs, but let’s talk about fakes as some might prefer to use them instead:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
… and you could also achieve the same result with sinon:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Be very careful with fakes as they can quickly become extremely complicated. If you can use stubs instead – please do. If you can’t then perhaps it just highlighted that the thing you’re trying to test is too complex and needs breaking down?
Techniques described in this article can be applied to both front-end and back-end (node.js) development, same applies to tools I used in the above examples, namely:
- sinon.js – test doubles library
- chai.js and its bdd-style assertion library
- sinon-chai – sinon.js assertions for chai
Final thoughts and a word of warning
Bear in mind that the more you spy and mock, the tighter you couple your tests to the implementation of your system. This leads to fragile tests that may break for reasons that shouldn’t break them.
Test doubles should be used with care and applied only when they’re the best tool for the task at hand. Just like with any other tool in your development tool belt: don’t use a screwdriver to knock in a nail just because you got a new screwdriver and want to try it out ;–)
The concept of “mock objects” was originally introduced by Tim Mackinnon, Steve Freeman and Philip Craig in their paper Endo-Testing: Unit Testing with Mock Objects. Steve Freeman is a co-author of the GOOSE book. Again, highly recommended.↩