// Awesomeness first const _ = require('lodash'); const proxyquire = require('proxyquire'); const sinon = require('sinon'); // Mocha + Chai are used here but feel free to use your test framework of choice ! const chai = require('chai'); const chaiAsPromised = require('chai-as-promised'); chai.use(chaiAsPromised); // Start with defining your module name const testedModuleName = 'path/to/my/tested/module'; // This function is used as a default stub to force you to override it while adding tests const stubMe = fnName => () => { throw new Error('Please stub this function call: ' + fnName); }; /** * First you prepare your external dependencies to be mocked * If any external dependency is added to the tested module, you have to add its name here as well */ const dep1 = 'path/to/dep1'; const dep2 = 'path/to/dep2'; /** * Then you prepare a higher order function that will help you easily override the default stub * If any external dependency is added to the tested module, you have to add its called functions here as well * As you can see, all default stubs are throwing functions, to ensure you will properly stub in the context of each test, * and that each test will be independent from the others */ const createStubs = customStubs => _.defaults({}, customStubs, { [dep1]: { fnA: stubMe('fnA') }, [dep2]: { fnB: stubMe('fnB'), fnC: stubMe('fnC') } }); // Then you can use it in your tests :3 describe('myAwesomeFunction()', () => { const m = proxyquire(testedModuleName, createStubs({ [dep1]: { fnA: () => 'foobar' }, // Here dep2.fnB is not stubbed because it's not used by myAwesomeFunction [dep2]: { fnC: () => Promise.resolve('barfob') } })); before(() => { // Stub here all your module functions that the tested function uses (internal dependencies) sinon.stub(m, 'aLocalFunctionMyAwesomeFunctionUses', () => true); }); after(() => { // Restore to ensure you don't silently impact other tests (that should stub this function too !) m.aLocalFunctionMyAwesomeFunctionUses.restore(); }); it('should do stuff', () => expect( m.myAwesomeFunction('foo') ) .to.eventually.be.eql('bar') ); }); // That's all, folks ! :3