Skip to content

Instantly share code, notes, and snippets.

@froots
Last active June 6, 2018 23:27
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save froots/d573ad1e0481c7a67041471617f3de55 to your computer and use it in GitHub Desktop.
Save froots/d573ad1e0481c7a67041471617f3de55 to your computer and use it in GitHub Desktop.
Stubbing module dependencies in ES2015 unit tests using Sinon.js or TestDouble.js.

I was all set up for the pain of stubbing Node.js dependencies, but you can just use the import * as blah from 'blah' syntax to obtain an object with properties that can be stubbed using either TestDouble or Sinon.

The object is shared between modules at runtime, so modifications will propagate everywhere. Be sure the use Sinon's restore() or TestDouble's reset() methods to return the module to it's normal state so that it doesn't bleed into other tests.

If you need to stub a default module function, then use this import

import * as kphToMphModule from '../src/kphToMph';

and stub with testdouble like this:

const kphToMph = td.replace(kphToMphModule, 'default');

or with Sinon:

const kphToMph = sinon.stub(kphToMphModule, 'default');

// in /src directory
import {kphToMph} from './convert';
class Car {
constructor(speed) {
this.speed = speed;
}
getSpeedInMph() {
return kphToMph(this.speed);
}
}
export default Car;
// In /src directory
export function kphToMph(speed) {
// Real function here
return 0;
}
{
"devDependencies": {
"babel-cli": "^6.14.0",
"babel-preset-es2015": "^6.14.0",
"babel-register": "^6.14.0",
"mocha": "^3.0.2",
"sinon": "^1.17.5",
"testdouble": "^1.6.1"
},
"scripts": {
"test": "mocha --compilers js:babel-register"
}
}
// Goes in /test directory
import assert from 'assert';
import sinon from 'sinon';
// Subject
import Car from '../src/Car';
// Dependency of subject - import whole module as an object
import * as convert from '../src/convert';
describe('Car', () => {
afterEach(() => {
// Need to reset any replaced methods
convert.kphToMph.restore();
});
it('returns speed converted to MPH', () => {
// Replace the internal dependency of Car
const getSpeedInMph = sinon.stub(convert, 'kphToMph');
// When the dependent method is called, return a fixed value
getSpeedInMph.withArgs(50).returns(31);
const car = new Car(50);
// Call the function that uses the dependency
// This will now call the replaced function
const actual = car.getSpeedInMph();
// Our expected value
const expected = 31;
assert.equal(actual, expected);
});
});
// Goes in /test directory
import assert from 'assert';
import td from 'testdouble';
// Subject
import Car from '../src/Car';
// Dependency of subject - import whole module as an object
import * as convert from '../src/convert';
describe('Car', () => {
afterEach(() => {
// Need to reset any replaced methods
td.reset();
});
it('returns speed converted to MPH', () => {
// Replace the internal dependency of Car
const kphToMph = td.replace(convert, 'kphToMph');
// When the dependent method is called, return a fixed value
td.when(kphToMph(50)).thenReturn(31);
const car = new Car(50);
// Call the function that uses the dependency
// This will now call the replaced function
const actual = car.getSpeedInMph();
// Our expected value
const expected = 31;
assert.equal(actual, expected);
});
});
@Strajk
Copy link

Strajk commented Sep 8, 2016

Does not work with add-module-exports preset, found out a hard way :(

https://ide.c9.io/strajk/node-stubbing-module-deps

@searls
Copy link

searls commented Sep 18, 2016

looks pretty cool. unsure i'll be able to do any better with testdouble.js since import is (theoretically) totally static, even if there might be a sneaky way to accomplish this as long as folks are transpiling it in

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment