npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

sinon-helpers

v2.0.12

Published

Create easily configurable sinon stubs that mimic constructors and keep track of their instances

Downloads

292

Readme

sinon-helpers

Create easily configurable sinon stubs that mimic constructors and keep track of their instances.

npm JavaScript Style Guide npm semantic-release

If updating from v1, please see updating from v1 to v2.

Motivation

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.

Approaches without sinon-helpers

  • 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.

What sinon-helpers offers

  • 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!

Installation

npm install --save-dev sinon-helpers

or

yarn add --dev sinon-helpers

Usage

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)

API

getStubConstructor(<OriginalConstructor>)

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.

StubConstructor API

A StubConstructor has the following methods and fields:

  • .withInit(onInit)
    Each time a new instance is created, onInit(instance) 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
    An array of all instances created with the stub constructor. Contains null if the constructor is called without new.
  • .getInstance()
    Throws an error if no or more than one instance has been created. Otherwise, returns the instance created.
  • .getInstance(index)
    Throws an error if not at least index instances have been created. Otherwise, returns the instance index.
  • .args
    An array of arrays containing the arguments of each constructor call.

getSpyConstructor(OriginalConstructor)

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.

SpyConstructor API

A SpyConstructor has the following methods and fields:

  • .withInit(onInit)
    When a new instance is created, onInit(instance) is called receiving the new instance as parameter; this enables you to perform manual post-processing before the instance is returned.
  • .instances
    An array of all instances created with the spy constructor. Contains null if the constructor is called without new.
  • .getInstance()
    Throws an error if no or more than one instance has been created. Otherwise, returns the instance created.
  • .getInstance(index)
    Throws an error if not at least index instances have been created. Otherwise, returns the instance index.
  • .args
    An array of arrays containing the arguments of each constructor call.

Migrating from v1 to v2

  • Instead of getInstances(), use instances to access the array of instances.
  • Instead of getInstancesArgs(), 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.

Contributing

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!