JavaScript Testing Tool Showdown: Sinon.js vs testdouble.js
When unit testing real-world code, there are many situations that make tests hard to write. How do you check if a function was called? How do you test an Ajax call? Or code using setTimeout
? That’s when you use test doubles — replacement code that makes hard to test things easy to test.
For many years, Sinon.js has been the de-facto standard in JavaScript tests for creating test doubles. It’s a must-have tool for any JavaScript developer writing tests, as without it, writing tests for real applications would be nigh impossible.
Recently, a new library, aptly named testdouble.js, has been making waves. It boasts a similar feature set as Sinon.js, with a few differences here and there.
In this article, we’ll look into what both Sinon.js and testdouble.js offer, and compare their respective pros and cons. Will Sinon.js remain the superior choice, or will the challenger take the prize?
Note: If you’re unfamiliar with test doubles, I recommend reading my Sinon.js tutorial first. It will help you better understand the concepts we’ll be talking about here.
Terminology Used in This Article
To ensure it’s easy to understand what is being discussed, here’s a quick overview of the terminology used. These are the definitions for Sinon.js, and they can be slightly different elsewhere.
- A test double is a replacement for a function used during a test. It can refer to any of the three types mentioned below.
- A spy is a test double which allows the checking of effects without affecting the behavior of the target function.
- A stub is a test double which replaces the target function’s behavior with something else, such as returning a value.
- A mock is a different approach to stubs. Mocks contain built-in verification and can be used instead of a separate assertion.
It should be noted that one of the goals of testdouble.js is to reduce the confusion between this type of terminology.
Sinon.js and testdouble.js at a Glance
Let’s begin with a look at how Sinon.js and testdouble.js compare in basic usage.
Sinon has three separate concepts for test doubles: Spies, stubs and mocks. The idea is that each represents a different usage scenario. This makes the library more familiar to those coming from other languages or who have read books using the same terminology, such as xUnit Test Patterns. But the other side is that these three concepts can also make Sinon more difficult to understand when first using it.
Here is a basic example of Sinon usage:
//Here's how we can see a function call's parameters:
var spy = sinon.spy(Math, 'abs');
Math.abs(-10);
console.log(spy.firstCall.args); //output: [ -10 ]
spy.restore();
//Here's how we can control what a function does:
var stub = sinon.stub(document, 'createElement');
stub.returns('not an html element');
var x = document.createElement('div');
console.log(x); //output: 'not an html element'
stub.restore();
In contrast, testdouble.js opts for an API which is more straightforward. Instead of using concepts like spies or stubs, it uses language much more familiar to JavaScript developers, such as td.function
, td.object
and td.replace
. This makes testdouble potentially easier to pick up, and better suited to certain tasks. But on the other hand, some more advanced uses may not be possible at all (which is sometimes intentional).
Here’s what testdouble.js looks in use:
//Here's how we can see a function call's parameters:
var abs = td.replace(Math, 'abs');
Math.abs(-10);
var explanation = td.explain(abs);
console.log(explanation.calls[0].args); //output: [ -10 ]
//Here's how we can control what a function does:
var createElement = td.replace(document, 'createElement');
td.when(createElement(td.matchers.anything())).thenReturn('not an html element');
var x = document.createElement('div');
console.log(x); //output: 'not an html element'
//testdouble resets all testdoubles with one call, no need for separate cleanup
td.reset();
The language used by testdouble is more straightforward. We “replace” a function instead of “stubbing” it. We ask testdouble to “explain” a function to get information from it. Other than this, so far it’s fairly similar to Sinon.
This also extends to creating “anonymous” test doubles:
var x = sinon.stub();
vs.
var x = td.function();
Sinon’s spies and stubs have properties which offer more information about them. For example, Sinon provides properties such as stub.callCount
, and stub.args
. In testdouble’s case, we get this information from td.explain
:
//we can give a name to our test doubles as well
var x = td.function('hello');
x('foo', 'bar');
td.explain(x);
console.log(x);
/* Output:
{
name: 'hello',
callCount: 1,
calls: [ { args: ['foo', 'bar'], context: undefined } ],
description: 'This test double `hello` has 0 stubbings and 1 invocations.nnInvocations:n - called with `("foo", "bar")`.',
isTestDouble: true
}
*/
One of the bigger differences relates to how you set up your stubs and verifications. With Sinon, you chain commands after a stub, and use an assertion to verify the result. testdouble.js simply has you show it how you want the function to be called — or how to “rehearse” the function call.
var x = sinon.stub();
x.withArgs('hello', 'world').returns(true);
var y = sinon.stub();
sinon.assert.calledWith(y, 'foo', 'bar');
vs.
var x = td.function();
td.when(x('hello', 'world')).thenReturn(true);
var y = td.function();
td.verify(y('foo', 'bar'));
This can make testdouble’s API easier to understand, since you don’t need to know what operations you can chain and when.
Comparing Common Testing Tasks in More Detail
On a high level both libraries are reasonably similar. But what about common testing tasks that you might need to do in a real project? Let’s take a look at a few cases where the differences start to show.
Continue reading %JavaScript Testing Tool Showdown: Sinon.js vs testdouble.js%
LEAVE A REPLY
You must be logged in to post a comment.