Create easily configurable sinon stubs that mimic constructors and keep track of their instances
npm install sinon-helpersCreate easily configurable sinon stubs that mimic constructors and keep track of their instances.




If updating from v1, please see updating from v1 to v2.
Especially when working with the new ES6 classes, a common problem in unit testing is to find out if a module creates
instances of a class using the right constructor arguments and which methods are called on these instances. Moreover,
it would be nice to be able to control that these classes are properly stubbed i.e. that in our tests, none of the
original class code is executed.
New test dependencies can be easily injected with rewire or
proxyquire for node testing and
inject-loader or
babel-plugin-rewire for webpack
testing. The question remains how the stated goal can be achieved using a mocking library such as
sinon.
In the examples, we want to mock a constructor MyConstructor.
- Replace MyConstructor by sinon.stub(MyConstructor). That way, we can find out which parameters are used to create
instances. The instances, however, will feature none of the methods of MyConstructor.
- Do the same using sinon.spy(MyConstructor) instead. But now the original code is executed as well and we still
cannot test method invocations.
- To test method invocations, we could stub methods of the prototype i.e.
sinon.stub(MyConstructor.prototype, 'myMethod') (do not forget to remove your stub after the test!), or if
MyConstructorStub = sinon.stub(MyConstructor), we could use MyConstructorStub.prototype.myMethod = sinon.stub() to
add the corresponding stubs. Now, however, all instances share the same stubs and we cannot match stub invocations with
the instances on which they were invoked.
To really solve this problem, we will need to create our own custom constructor. sinon-helpers is a library that
offers an easy and generic solution to this problem.
- MyStubConstructor = getStubConstructor(MyConstructor) generates a new constructor using MyConstrucor as a template. This means that new instances will contain all methods of MyConstructor as stubs including inherited and non-enumerable methods but skipping getters.
- MyStubConstructor.instances holds an array of all instances that have been created using this constructor. If you expect only a single instance to be created, you can retrieve it directly via MyStubConstructor.getInstance(), which will also throw an error if more than one or no instance has been created. Thus you can test for all instances separately which methods have been invoked in which way.
- MyStubConstructor.args returns an array of arrays of arguments used to create the instances.
- As the prototype is not modified, you do not have to clean up your stubs after the test!
``bash`
npm install --save-dev sinon-helpers
or
`bash`
yarn add --dev sinon-helpers
`js
var sh = require('sinon-helpers') // CommonJS
import * as sh from 'sinon-helpers' // ES6
// alternative: import {getStubConstructor, getSpyConstructor} from 'sinon-helpers'
// Create a constructor mimicking a given constructor
var MyStubConstructor = sh.getStubConstructor(MyConstructor)
// You can initialize your stub to e.g. provide return values for your methods
// or add fields that are not part of the prototype
var MyStubConstructor = sh.getStubConstructor(MyConstructor).withInit(instance => {
// this assumes MyConstructor.prototype.myMethod exists
instance.myMethod.returns(42)
instance.additionalField = 'added'
})
// Create a constructor that calls through to the original constructor
// with spies on all methods instead of stubs
var MySpyConstructor = sh.getSpyConstructor(MyConstructor)
`
Returns a StubConstructor mimicking the given constructor OriginalConstructor. When called with new, this constructor creates an object with stubs for any methods of the prototype object of ConstructorName.
If you call getStubConstructor without any arguments, you receive a StubConstructor without any pre-defined methods. A StubConstructor features methods to configure and query the created instances.
A StubConstructor has the following methods and fields:
- .withInit(onInit) onInit(instance)
Each time a new instance is created, is called receiving the new instance as parameter; this enables you to perform manual post-processing like configuring return values and adding additional fields before the instance is returned..instances
- null
An array of all instances created with the stub constructor. Contains if the constructor is called without new..getInstance()
- .getInstance(index)
Throws an error if no or more than one instance has been created. Otherwise, returns the instance created.
- index
Throws an error if not at least instances have been created. Otherwise, returns the instance index..args
-
An array of arrays containing the arguments of each constructor call.
Returns a SpyConstructor of the given constructor OriginalConstructor. A SpyConstructor is similar to a StubConstructor except for the following differences:
- The OriginalConstructor is called when creating a new instance.
- Methods are not stubbed but spied on.
- Which methods are spied on is not determined by looking at the prototype but by looking at what methods are actually present after the original constructor has run.
This is useful if you need to preserve the constructor's functionality while being able to track its instances. Note however that having to rely on SpyConstructors instead of StubConstructors may be an indication of strong couplings in your software that are generally a sign that your architecture could be improved.
A SpyConstructor has the following methods and fields:
- .withInit(onInit) onInit(instance)
When a new instance is created, is called receiving the new instance as parameter; this enables you to perform manual post-processing before the instance is returned..instances
- null
An array of all instances created with the spy constructor. Contains if the constructor is called without new..getInstance()
- .getInstance(index)
Throws an error if no or more than one instance has been created. Otherwise, returns the instance created.
- index
Throws an error if not at least instances have been created. Otherwise, returns the instance index..args
-
An array of arrays containing the arguments of each constructor call.
- Instead of getInstances(), use instances to access the array of instances.getInstancesArgs()
- Instead of , use args to access the arguments used to create instances.afterCreation()
- is now called withInit().withMethods
- , withStubs: These methods have been removed in favour of using withInit()`, which is much more powerful.
Feel like this library could do more for you? Found an issue with your setup? Want to get involved? Then why not contribute by raising an issue or creating a pull-request!