Sinon JS Tutorial: A Beginner’s Guide To Mocking

The Testim blog has been publishing a lot of posts about JavaScript testing related concepts and tools. We've covered Jest…

Testim
By Testim,

The Testim blog has been publishing a lot of posts about JavaScript testing related concepts and tools. We’ve covered Jest testing, Cucumber.js, and load testing with Artillery, among many other topics. Today, we carry on with the trend by covering yet another JavaScript testing-related tool: Sinon JS.

Sinon JS is a popular JavaScript library that lets you replace complicated parts of your code that are hard to test for “placeholders,” so you can keep your unit tests fast and deterministic, as they should be.

If the line above didn’t make much sense, there’s no reason to despair, since we’ll explain what these placeholders are and why we need them before getting to the tutorial. Let’s get started.

Sinon JS: An Intro

As promised, we start with an overview of Sinon JS. But to be able to understand what this tool is and why it’s useful, you first have to understand what test doubles are and the role they play in unit testing, so let’s start with that.

The Importance of Test Doubles in Unit Testing

To understand what test doubles are and why they matter, you must understand unit testing and the main difference between it and other types of testing—such as integration or end-to-end testing.

Unit Tests Can’t Play Outside

Whenever I have to explain what unit tests are, I like to resort to Michael Feather’s definition, which I really like because it’s a negative definition in a way: it explains what unit tests are not.

“A test is not a unit test if:

  • It talks to the database
  • It communicates across the network
  • It touches the file system
  • It can’t run at the same time as any of your other unit tests
  • You have to do special things to your environment (such as editing config files) to run it.”

In other words, we can say that unit tests can’t have side-effects. Of course, there are reasons for that. By making our tests neither consume nor create side-effects, we can have fast tests (since they don’t do I/O) that are also deterministic (the tested functions always return the same result for the same input) and can be run concurrently.

Real Code Does Play Outside

The fact that unit tests don’t mesh well with side-effects presents an interesting challenge when writing tests. Why?

Well, side-effects are freaking useful, that’s why! Though they might be problematic at times, side-effects are indispensable in any useful, real-life application. Real-world apps talk to databases and communicate across networks and interact with the filesystem and do all kinds of things that could be considered messy, but guess what? We love it because those messy things are useful.

We could go one step further and argue that the reason why applications exist is to generate side-effects. Even displaying information through a UI is a form of side-effect. So, a completely side-effect free application would also be a completely useless application.

Test Doubles To the Rescue

That’s where the concept of test doubles comes in handy. “Test double” is the generic term for an object or procedure you use to replace a real object or procedure that’s hard to test. The test double looks and behaves as if it was the real thing, but without incurring any side-effects whatsoever. The code that interacts with the double is none the wiser and “thinks” it’s dealing with the real thing.

Confusing? Don’t worry. Examples will come and make things clearer.

The Different Types of Testing Doubles

There are several types of test doubles, mocks, and stubs being the most well-known. Even though people often use the terms interchangeably, they’re not the same thing.

Here’s a list of the main types of test doubles, as defined by Gerard Meszaros (author of the book XUnit Testing Patterns) and shared by Martin Fowler:

  • Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists.
  • Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an in memory database is a good example).
  • 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.
  • Spies are stubs that also record some information based on how they were called. One form of this might be an email service that records how many messages it was sent.
  • Mocks are what we are talking about here: objects pre-programmed with expectations which form a specification of the calls they are expected to receive.

When To Use (And Not To Use) Each Type of Test Doubles

As you’ve just seen, there are several different types of test doubles. And although people sometimes use their names interchangeably, they’re not the same thing at all. Because of all that, newcomers to the world of unit testing often get confused about the different flavors of test doubles—and understandably so.

Because of that, we’re going to offer a quick guide on the differences between the available test doubles, to help you understand the scenarios in which each one is the more adequate choice. We’ll not cover all of the existing test doubles. Instead, we’ll focus on the three types provided by Sinon: stubs, test spies, and mocks.

Stubs

Of all three types of test doubles we’re covering, stubs are the simplest one. We can think of a stub as a dumb object that simply implements the interface of the object we’re stubbing out. It doesn’t try to be a working implementation. That is, you wouldn’t be able to use a stub object in production code.

Let’s think of a completely language-agnostic stub example. Suppose you have a function that, after successfully completing its task, needs to print some documents. You don’t want to go around and actually waste energy and ink printing those documents. To be able to test the function, you could replace the object that does the printing with a stub that conforms to the same interface. This stub object needs no implementation at all: it can happily do nothing while the calling code is none the wiser about talking to a fake.

So, when to use stubs? In short, you use stubs when:

Test Spies

Test spies are like stubs, but a bit more sophisticated. They do more than just adhering to the expected interface. They can also record useful information about the interactions that happened between them and the test code.

So, continuing our previous example, a spy printer object could keep an internal log of the documents it “printed.” That way, tests would be able to assert against said log to verify whether a particular document was sent to be printed out.

When to use spies?

  • You also don’t need your test double to have any real implementation.
  • You want to know about the interactions that occurred with your test double (e.g. a given function was called, what parameters were passed, etc.)
  • The unit testing style you use is a little more biased towards behavior rather than state verification.
Mocks

Finally, mocks are the final type of test double we’re covering. Mocks are all about interaction. When you use Mocks you mostly don’t care about state verifications. Instead, you care about how your object interacted with the test code. Did the function “x” get called exactly the number of times we expected? Were all of the expected functions called in the exact right order? Those are the kinds of questions that mock objects help you answer.

So, you should use mocks in stations where what you care about the most are the interactions and the behavior of your object.

Before we go on to our SinonJs tutorial, here’s a caveat about mocks: relying heavily on them can lead to a test suite riddled with brittle tests. That, in turn, will turn test maintenance into a heavy burden to your team.

Why does that happen? Simply put, relying too heavily on mocking can lead to over-specified unit tests. You end up with tests that are tightly-coupled—i.e. they know “too much”—about implementation details of the mocked object. When that happens, even the minimum changes to the objects will result in failed tests.

Remember: unit test best practices say that unit tests should be simple, readable, and not duplicate production logic. These are all consequences you get when writing over-specified mocked tests.

Test Doubles in JavaScript? Say Hello To Sinon JS

SinonJS is a JavaScript library that provides standalone test spies, stubs, and mocks. It’s very flexible and easy to use since you can combine it with any testing framework. For this post, we’re using Mocha.

Time for Our Sinon JS Tutorial

With the theory and fundamentals out of the way, we’re finally ready for our Sinon JS tutorial. We’ll start by installing the necessary tools. For starters, you’re going to need Node.js, since we’re using npm to install the tools we need. So, if you don’t already have it, go ahead and download and install Node.js so you can follow the tutorial.

Installing Mocha

Node.js installed? Great, let’s continue. The first tool we’re going to install is Mocha. Open your terminal and execute the following command:

npm install -g mocha

As you can see, we’ve used the “-g” option. That means that we want npm to install mocha globally—instead of just for a single product—so we can access it via the command line. To check if the installation was successful, run this:

mocha -version

That should return the current version, which, by the time of this writing, is 8.2.0. Now, let’s create a directory for our sample project:

mkdir sinon-demo

Let’s enter the directory, initiate the new project, and add mocha to it:

cd sinon-demo
npm init
npm install --save-dev mocha

After running the npm init command, npm will ask you several questions, so it can configure your package.json file. For our purposes here, those properties don’t really matter. So, to save time, you can just press enter after each question to go with the default values.

Creating Our First Mocha Test

Now we’re ready to create a simple test just to get a taste of how to work with Mocha. Create a directory called test and place a new file inside it called first.js. Open first.js and paste the following content on it:

const assert = require("assert");

describe("just a silly test", function() {
  it("checks a sum", function() {
    assert.equal(2 + 2, 4);
  });
});

As you can see, our test is super simple. It just checks whether two plus two is four. You run the test simply by running mocha. By default, Mocha looks for tests inside a test named test. Since we’re following that convention, no additional action is required. If everything went right, that’s what you’ll see:

Let’s now change the test, so it fails, just to see how that looks like. Go back to the test and replace the four with any other number. Then save the file and run the test again. This is my result:

Installing SinonJS and Creating an “Untestable” Function

It’s now finally the time to install SinonJS. Run the following command:

npm-install --save-dev sinon

After the installation is completed, we’re going to create a function to test. Go to the root of the project, and create a file called greeter.js and paste the following content on it:

function greet(name) {    
    var options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
    var now  = new Date();
    var formattedDate = now.toLocaleDateString("en-US", options);
    return `Hello, ${name}! Today is ${formattedDate}`;
}

What this function does is something very simple: after receiving a name, it returns a string in which it greets the person and informs the current day. So, that’s what I get when I pass my name to it:

"Hello, Carlos! Today is Thursday, October 22, 2020"

Wouldn’t it be great if we could test this function? Yes, of course. But currently, we can’t, since it depends on the current time. How do we test it, then?

Putting Sinon To Work

We’re going to use SinonJS for that. By providing a “fake” current time, we’ll be able to check if the function works properly. The test is going to be fast and deterministic, as unit tests ought to be. It also won’t rely on any external volatile dependence. So, let’s get to it.

First of all, we’re going to make some small changes to our greeter.js file, so the function can be exported. Go there and replace its content with this:

(function(exports) {

  function greet(name) {    
    var options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
    var now  = new Date();
    var formattedDate = now.toLocaleDateString("en-US", options);
    return `Hello, ${name}! Today is ${formattedDate}`;
}

exports.greet = greet;

})(this);

Now, let’s change our test. Go back to test/first.js, and replace its content with the code below:

const assert = require("assert");
const greeter = require("../greeter.js");

describe("testing the greeter", function() {
  it("checks the greet function", function() {
    assert.equal(greeter.greet('Alice'), 'Hello, Alice! Today is Friday, January 15, 2021.');
  });
});

Save and run the test again. It will fail (unless it’s actually January 15, 2021, when you’re reading this.) Here is how it looks like to me:

To make this test pass, we need a way to pretend we’re on a different day. Can SinonJS help us with that? You bet it can!

Let’s change the test file again, so it looks like this:

const assert = require("assert");
const greeter = require("../greeter.js");
const sinon = require("sinon");

describe("testing the greeter", function() {
  it("checks the greet function", function() {
    var clock = sinon.useFakeTimers(new Date(2021, 0, 15));
    assert.equal(greeter.greet('Alice'), 'Hello, Alice! Today is Friday, January 15, 2021');
    clock.restore();
  });
});

The changes were minimal. First, right at the start of the file, we require Sinon, in order to use it afterward. In the test method, just before the assertion, we call the “useFakeTimers” function, passing January 15, 2021, as the date. Remember that in JavaScript, months start in zero, so January is number zero instead of the expected one.

After the assertion, we restore the clock, so the time can go back to normal and not affect the other test cases. Easy, right? Now let’s run the test to see it passing:

sinon js tutorial

Fake It ‘Til You Make It

Automated testing is crucial for writing high-quality apps, and unit tests are one of the most important types of tests you can use.

However, unit testing real, complex applications isn’t always easy. Useful code consumes and/or generates side-effects, takes dependencies on environmental data—like the current time or the current locale—and so on. In order to solve these problems, we use test doubles.

This post was a brief introduction to using test doubles/fakes/mocks in JavaScript. Despite being a very simple tutorial, you now have a good understanding of the principles and motivations behind using test doubles. You also have a working SinonJS environment, which you can use to practice and learn more.

Also, stay tuned to this blog, since we’ll continue publishing interesting articles about testing-related subjects. Thanks for reading, and we’ll see you next time.

This post was written by Carlos Schults. Carlos is a .NET software developer with experience in both desktop and web development, and he’s now trying his hand at mobile. He has a passion for writing clean and concise code, and he’s interested in practices that help you improve app health, such as code review, automated testing, and continuous build.