Sinon Tutorial: JavaScript Testing with Mocks, Spies & Stubs

Share this article

This article was peer reviewed by Mark Brown and MarcTowler. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

One of the biggest stumbling blocks when writing unit tests is what to do when you have code that’s non-trivial.

In real life projects, code often does all kinds of things that make testing hard. Ajax requests, timers, dates, accessing other browser features… or if you’re using Node.js, databases are always fun, and so is network or file access.

All of these are hard to test because you can’t control them in code. If you’re using Ajax, you need a server to respond to the request, so as to make your tests pass. If you use setTimeout, your test will have to wait. With databases or networking, it’s the same thing — you need a database with the correct data, or a network server.

Real-life isn’t as easy as many testing tutorials make it look. But did you know there is a solution?

By using Sinon, we can make testing non-trivial code trivial!

Let’s find out how.

What Makes Sinon so Important and Useful?

Put simply, Sinon allows you to replace the difficult parts of your tests with something that makes testing simple.

When testing a piece of code, you don’t want to have it affected by anything outside the test. If something external affects a test, the test becomes much more complex and could fail randomly.

If you want to test code making an Ajax call, how can you do that? You need to run a server and make sure it gives the exact response needed for your test. It’s complicated to set up, and makes writing and running unit tests difficult.

And what if your code depends on time? Let’s say it waits one second before doing something. What now? You could use a setTimeout in your test to wait one second, but that makes the test slow. Imagine if the interval was longer, for example five minutes. I’m going to guess you probably don’t want to wait five minutes each time you run your tests.

By using Sinon, we can take both of these issues (plus many others), and eliminate the complexity.

How Does Sinon Work?

Sinon helps eliminate complexity in tests by allowing you to easily create so called test-doubles.

Test-doubles are, like the name suggests, replacements for pieces of code used in your tests. Looking back at the Ajax example, instead of setting up a server, we would replace the Ajax call with a test-double. With the time example, we would use test-doubles to allow us to “travel forwards in time”.

It may sound a bit weird, but the basic concept is simple. Because JavaScript is very dynamic, we can take any function and replace it with something else. Test-doubles just take this idea a little bit further. With Sinon, we can replace any JavaScript function with a test-double, which can then be configured to do a variety of things to make testing complex things simple.

Sinon splits test-doubles into three types:

  • Spies, which offer information about function calls, without affecting their behavior
  • Stubs, which are like spies, but completely replace the function. This makes it possible to make a stubbed function do whatever you like — throw an exception, return a specific value, etc
  • Mocks, which make replacing whole objects easier by combining both spies and stubs

In addition, Sinon also provides some other helpers, although these are outside the scope of this article:

With these features, Sinon allows you to solve all of the difficult problems external dependencies cause in your tests. If you learn the tricks for using Sinon effectively, you won’t need any other tools.

Installing Sinon

First off we need to install Sinon.

For Node.js testing:

  1. Install Sinon via npm using npm install sinon
  2. Require Sinon in your test with var sinon = require('sinon');

For browser based testing:

  1. You can either install Sinon via npm with npm install sinon, use a CDN, or download it from Sinon’s website
  2. Include sinon.js in your test runner page.

Getting Started

Sinon has a lot of functionality, but much of it builds on top of itself. You learn about one part, and you already know about the next one. This makes Sinon easy to use once you learn the basics and know what each different part does.

We usually need Sinon when our code calls a function which is giving us trouble.

With Ajax, it could be $.get or XMLHttpRequest. With time, the function might be setTimeout. With databases, it could be mongodb.findOne.

To make it easier to talk about this function, I’m going to call it the dependency. The function we are testing depends on the result of another function.

We can say, the basic use pattern with Sinon is to replace the problematic dependency with a test-double.

  • When testing Ajax, we replace XMLHttpRequest with a test-double which pretends to make an Ajax request
  • When testing time, we replace setTimeout with a pretend timer
  • When testing database access, we could replace mongodb.findOne with a test-double which immediately returns some fake data

Let’s see how that works in practice.

Spies

Spies are the simplest part of Sinon, and other functionality builds on top of them.

The primary use for spies is to gather information about function calls. You can also use them to help verify things, such as whether a function was called or not.

var spy = sinon.spy();

//We can call a spy like a function
spy('Hello', 'World');

//Now we can get information about the call
console.log(spy.firstCall.args); //output: ['Hello', 'World']

The function sinon.spy returns a Spy object, which can be called like a function, but also contains properties with information on any calls made to it. In the example above, the firstCall property has information about the first call, such as firstCall.args which is the list of arguments passed.

Although you can create anonymous spies as above by calling sinon.spy with no parameters, a more common pattern is to replace another function with a spy.

var user = {
  ...
  setName: function(name){
    this.name = name;
  }
}

//Create a spy for the setName function
var setNameSpy = sinon.spy(user, 'setName');

//Now, any time we call the function, the spy logs information about it
user.setName('Darth Vader');

//Which we can see by looking at the spy object
console.log(setNameSpy.callCount); //output: 1

//Important final step - remove the spy
setNameSpy.restore();

Replacing another function with a spy works similarly to the previous example, with one important difference: When you’ve finished using the spy, it’s important to remember to restore the original function, as in the last line of the example above. Without this your tests may misbehave.

Spies have a lot of different properties, which provide different information on how they were used. Sinon’s spy documentation has a comprehensive list of all available options.

In practice, you might not use spies very often. You’re more likely to need a stub, but spies can be convenient for example to verify a callback was called:

function myFunction(condition, callback){
  if(condition){
    callback();
  }
}

describe('myFunction', function() {
  it('should call the callback function', function() {
    var callback = sinon.spy();

    myFunction(true, callback);

    assert(callback.calledOnce);
  });
});

In this example I am using Mocha as the test framework and Chai as the assertion library. If you would like to learn more about either of these, then please consult my previous article: Unit Test Your JavaScript Using Mocha and Chai.

See the Pen Sinon Tutorial: JavaScript Testing with Mocks, Spies & Stubs by SitePoint (@SitePoint) on CodePen.

Sinon’s Assertions

Before we carry on and talk about stubs, let’s take a quick detour and look at Sinon’s assertions.

In most testing situations with spies (and stubs), you need some way of verifying the result of the test.

We can use any kind of assertion to verify the results. In the previous example with the callback, we used Chai’s assert function which ensures the value is truthy.

assert(callback.calledOnce);

The problem with this is that the error message in a failure is unclear. You’ll simply be told “false was not true”, or some variation of that. As you can probably imagine, it’s not very helpful in finding out what went wrong, and you need to go look at the source code for the test to figure it out. Not fun.

To fix the problem, we could include a custom error message into the assertion.

assert(callback.calledOnce, 'Callback was not called once');

But why bother when we can use Sinon’s own assertions?

describe('myFunction', function() {
  it('should call the callback function', function() {
    var callback = sinon.spy();

    myFunction(true, callback);

    sinon.assert.calledOnce(callback);
  });
});

Using Sinon’s assertions like this gives us a much better error message out of the box. This becomes very useful when you need to verify more complex condition, such as the parameters to a function.

Here are some examples of other useful assertions provided by Sinon:

  • sinon.assert.calledWith can be used to verify the a function was called with specific parameters (this is probably the one I use most often)
  • sinon.assert.callOrder can verify functions were called in a specific order

As with spies, Sinon’s assertion documentation has all the options available. If you like using Chai, there is also a sinon-chai plugin available, which lets you use Sinon assertions through Chai’s expect or should interface.

Stubs

Stubs are the go-to test-double because of their flexibility and convenience. They have all the functionality of spies, but instead of just spying on what a function does, a stub completely replaces it. In other words, when using a spy, the original function still runs, but when using a stub, it doesn’t.

This makes stubs perfect for a number of tasks, such as:

  • Replacing Ajax or other external calls which make tests slow and difficult to write
  • Triggering different code paths depending on function output
  • Testing unusual conditions, for example what happens when an exception is thrown?

We can create stubs in a similar way to spies…

var stub = sinon.stub();

stub('hello');

console.log(stub.firstCall.args); //output: ['hello']

We can create anonymous stubs as with spies, but stubs become really useful when you use them to replace existing functions.

For example, if we have some code that uses jQuery’s Ajax functionality, testing it is difficult. The code sends a request to whatever server we’ve configured, so we need to have it available, or add a special case to the code to not do that in a test environment — which is a big no-no. You should almost never have test-specific cases in your code.

Instead of resorting to poor practices, we can use Sinon and replace the Ajax functionality with a stub. This makes testing it trivial.

Here’s an example function we’ll test. It takes an object as its parameter, and sends it via Ajax to a predefined URL.

function saveUser(user, callback) {
  $.post('/users', {
    first: user.firstname,
    last: user.lastname
  }, callback);
}

Normally, testing this would be difficult because of the Ajax call and predefined URL, but if we use a stub, it becomes easy.

Let’s say we want to ensure the callback function passed to saveUser is called correctly once the request finishes.

describe('saveUser', function() {
  it('should call callback after saving', function() {

    //We'll stub $.post so a request is not sent
    var post = sinon.stub($, 'post');
    post.yields();

    //We can use a spy as the callback so it's easy to verify
    var callback = sinon.spy();

    saveUser({ firstname: 'Han', lastname: 'Solo' }, callback);

    post.restore();
    sinon.assert.calledOnce(callback);
  });
});

See the Pen Sinon Tutorial: JavaScript Testing with Mocks, Spies & Stubs by SitePoint (@SitePoint) on CodePen.

Here, we replace the Ajax function with a stub. This means the request is never sent, and we don’t need a server or anything — we have full control over what happens in our test code!

As we want to ensure the callback we pass into saveUser gets called, we’ll instruct the stub to yield. This means the stub automatically calls the first function passed as a parameter to it. This mimics the behavior of $.post, which would call a callback once the request has finished.

In addition to a stub, we’re creating a spy in this test. We could use a normal function as the callback, but using a spy makes it easy to verify the result of the test using Sinon’s sinon.assert.calledOnce assertion.

In most cases when you need a stub, you can follow the same basic pattern:

  • Find the problematic function, such as $.post
  • Look at how it works so you can mimic it in the test
  • Create a stub
  • Set the stub to have the behavior you want in your test

The stub doesn’t need to mimic every behavior. Only the behavior you need for the test is necessary, and anything else can be left out.

Another common usage for stubs is verifying a function was called with a specific set of arguments.

For example, for our Ajax functionality, we want to ensure the correct values are being sent. Therefore, we could have something like:

describe('saveUser', function() {
  it('should send correct parameters to the expected URL', function() {

    //We'll stub $.post same as before
    var post = sinon.stub($, 'post');

    //We'll set up some variables to contain the expected results
    var expectedUrl = '/users';
    var expectedParams = {
      first: 'Expected first name',
      last: 'Expected last name'
    };

    //We can also set up the user we'll save based on the expected data
    var user = {
      firstname: expectedParams.first,
      lastname: expectedParams.last
    }

    saveUser(user, function(){} );
    post.restore();

    sinon.assert.calledWith(post, expectedUrl, expectedParams);
  });
});

See the Pen Sinon Tutorial: JavaScript Testing with Mocks, Spies & Stubs by SitePoint (@SitePoint) on CodePen.

Again, we create a stub for $.post(), but this time we don’t set it to yield. This test doesn’t care about the callback, therefore having it yield is unnecessary.

We set up some variables to contain the expected data — the URL and the parameters. It’s a good practice to set up variables like this, as it makes it easy to see at a glance what the requirements for the test are. It also helps us set up the user variable without repeating the values.

This time we used the sinon.assert.calledWith() assertion. We pass the stub as its first parameter, because this time we want to verify the stub was called with the correct parameters.

There’s also another way of testing Ajax requests in Sinon. This is by using Sinon’s fake XMLHttpRequest functionality. We won’t go into detail on it here, but if you want to learn how that works, see my article on Ajax testing with Sinon’s fake XMLHttpRequest.

Mocks

Mocks are a different approach to stubs. If you’ve heard the term “mock object”, this is the same thing — Sinon’s mocks can be used to replace whole objects and alter their behavior similar to stubbing functions.

They are primarily useful if you need to stub more than one function from a single object. If you only need to replace a single function, a stub is easier to use.

You should take care when using mocks! Because of their power, it’s easy to make your tests overly specific — test too many and too specific things — which risks making your tests unintentionally brittle.

Unlike spies and stubs, mocks have assertions built-in. You define your expected results up front by telling the mock object what needs to happen, and then calling the verification function at the end of the test.

Let’s say we’re using store.js to save things into localStorage, and we want to test a function related to that. We can use a mock to help testing it like so:

describe('incrementStoredData', function() {
  it('should increment stored value by one', function() {
    var storeMock = sinon.mock(store);
    storeMock.expects('get').withArgs('data').returns(0);
    storeMock.expects('set').once().withArgs('data', 1);

    incrementStoredData();

    storeMock.restore();
    storeMock.verify();
  });
});

See the Pen Sinon Tutorial: JavaScript Testing with Mocks, Spies & Stubs by SitePoint (@SitePoint) on CodePen.

When using mocks, we define the expected calls and their results using a fluent calling style as seen above. This is the same as using assertions to verify test results, except we define them up-front, and to verify them, we call storeMock.verify() at the end of the test.

In Sinon’s mock object terminology, calling mock.expects('something') creates an expectation. As in, the method mock.something() expects to be called. Each expectation, in addition to mock-specific functionality, supports the same functions as spies and stubs.

You may find that it’s often much easier to use a stub than a mock — and that’s perfectly fine. Mocks should be used with care.

For a full list of mock-specific functions, check Sinon’s mock documentation.

Important Best Practice: Use sinon.test()

There is one important best practice with Sinon that should be remembered whenever using spies, stubs or mocks.

If you replace an existing function with a test-double, use sinon.test().

In the earlier example, we used stub.restore() or mock.restore() to clean up after using them. This is necessary as otherwise the test-double remains in place, and could negatively affect other tests or cause errors.

But using the restore() function directly is problematic. It’s possible that the function being tested causes an error and ends the test function before restore() has been called!

We have two ways to solve this: We can wrap the whole thing in a try catch block. This allows us to put the restore() call in a finally block, ensuring it gets run no matter what.

Or, a better approach, we can wrap the test function with sinon.test()

it('should do something with stubs', sinon.test(function() {
  var stub = this.stub($, 'post');

  doSomething();

  sinon.assert.calledOnce(stub);
});

In the above example, note the second parameter to it() is wrapped within sinon.test(). The second thing of note is that we use this.stub() instead of sinon.stub().

Wrapping a test with sinon.test() allows us to use Sinon’s sandboxing feature, allowing us to create spies, stubs and mocks via this.spy(), this.stub() and this.mock(). Any test-doubles you create using sandboxing are cleaned up automatically.

Note that our example code above has no stub.restore() — it’s unnecessary thanks to the test being sandboxed.

If you use sinon.test() where possible, you can avoid problems where tests start failing randomly because an earlier test didn’t clean up its test-doubles due to an error.

Sinon Is Not Magic

Sinon does many things, and occasionally it might seem difficult to understand how it works. Let’s take a look at some plain JavaScript examples of how Sinon works, so we can get a better idea of what it does under the hood. This will help you use it more effectively in different situations.

We can create spies, stubs and mocks manually too. The reason we use Sinon is it makes the task trivial — creating them manually can be quite complicated, but let’s see how that works, to understand what Sinon does.

First, a spy is essentially a function wrapper:

//A simple spy helper
function createSpy(targetFunc) {
  var spy = function() {
    spy.args = arguments;
    spy.returnValue = targetFunc.apply(this, arguments);
    return spy.returnValue;
  };

  return spy;
}

//Let's spy on a simple function:
function sum(a, b) { return a + b; }

var spiedSum = createSpy(sum);

spiedSum(10, 5);

console.log(spiedSum.args); //Output: [10, 5]
console.log(spiedSum.returnValue); //Output: 15

We can get spy functionality quite easily with a custom function like so. But notice that Sinon’s spies provide a much wider array of functionality — including assertion support. This makes Sinon a lot more convenient.

What about a Stub Then?

To make a really simple stub, you can simply replace a function with a new one:

var stub = function() { };

var original = thing.otherFunction;
thing.otherFunction = stub;

//Now any calls to thing.otherFunction will call our stub instead

But again, there are several advantages Sinon’s stubs provide:

  • They have the full spy functionality in them
  • You can restore original behavior easily with stub.restore()
  • You can assert against Sinon stubs

Mocks simply combine the behavior of spies and stubs, making it possible to use their features in different ways.

Even though Sinon may sometimes seem like it does a lot of “magic”, this can be done fairly easily with your own code too, for the most part. Sinon is just much more convenient to use, than having to write your own library for the purpose.

Conclusion

Testing real-life code can sometimes seem way too complex and it’s easy to give up altogether. But with help from Sinon, testing virtually any kind of code becomes a breeze.

Just remember the main principle: If a function makes your test difficult to write, try replacing it with a test-double. This principle applies regardless of what the function does.

Looking to learn more about how to apply Sinon with your own code? Head over to my site and I’ll send you my free Sinon in the real-world guide, which includes Sinon best practices, and three real-world examples of how to apply it in different types of testing situations!

Frequently Asked Questions (FAQs) about Sinon.js Testing

What is the difference between mocks, spies, and stubs in Sinon.js?

In Sinon.js, mocks, spies, and stubs serve different purposes. Spies are functions that record arguments, return value, the value of this and exception thrown (if any) for all its calls. They can be used to track function calls and responses. Stubs are like spies, but with pre-programmed behavior. They also record information about how they were called, but unlike spies, they can be used to control a method’s behavior to force a method to throw an error or return a specific value. Mocks are fake methods (like spies) with pre-programmed behavior (like stubs) as well as pre-programmed expectations.

How can I use Sinon.js for unit testing in JavaScript?

Sinon.js is a powerful tool for creating spies, stubs, and mocks in your JavaScript tests. To use it, you first need to include it in your project, either by using a script tag in your HTML or by installing it via npm. Once it’s included, you can use its API to create and manage spies, stubs, and mocks. These can then be used to isolate the code you’re testing and ensure that it’s behaving as expected.

How can I create a spy in Sinon.js?

Creating a spy in Sinon.js is straightforward. You simply call the sinon.spy() function. This will return a spy function that you can use in your tests. The spy will record information about how it’s called, which you can then inspect in your tests. For example, you can check how many times the spy was called, what arguments it was called with, and what it returned.

How can I create a stub in Sinon.js?

To create a stub in Sinon.js, you call the sinon.stub() function. This will return a stub function that you can use in your tests. The stub will behave like a spy, recording information about how it’s called, but it also allows you to control its behavior. For example, you can make the stub throw an error, or return a specific value.

How can I create a mock in Sinon.js?

Creating a mock in Sinon.js involves calling the sinon.mock() function. This will return a mock object that you can use in your tests. The mock object behaves like a spy, recording information about how it’s called, and like a stub, allowing you to control its behavior. But it also allows you to set up expectations about how it should be called.

How can I use Sinon.js with other testing frameworks?

Sinon.js is designed to work with any JavaScript testing framework. It provides a standalone test framework, but it can also be integrated with other popular testing frameworks like Mocha, Jasmine, and QUnit. The Sinon.js documentation provides examples of how to integrate with these and other testing frameworks.

How can I restore a stub or spy to its original function?

If you’ve replaced a function with a stub or spy, you can restore the original function by calling the .restore() method on the stub or spy. This is useful if you want to clean up after a test, to ensure that the stub or spy doesn’t affect other tests.

How can I check if a spy was called with specific arguments?

Sinon.js provides several methods to check how a spy was called. For example, you can use the .calledWith() method to check if the spy was called with specific arguments. You can also use the .calledOnceWith() method to check if the spy was called exactly once with specific arguments.

How can I make a stub return a specific value?

You can make a stub return a specific value by using the .returns() method. For example, if you have a stub called myStub, you can make it return the value ‘foo’ by calling myStub.returns(‘foo’).

How can I make a stub throw an error?

You can make a stub throw an error by using the .throws() method. For example, if you have a stub called myStub, you can make it throw an error by calling myStub.throws(). By default, this will throw an Error object, but you can also make it throw a specific type of error by passing the error’s name as an argument.

Jani HartikainenJani Hartikainen
View Author

Jani has built all kinds of JS apps for more than 15 years. At his blog, he helps JavaScript developers learn to eliminate bad code so they can focus on writing awesome apps and solve real problems.

ajaxBDDchaijameshmochamocksSinonspiesstubsunit testing
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week