Skip to content

Instantly share code, notes, and snippets.

@indieisaconcept
Last active August 29, 2015 14:10
Show Gist options
  • Save indieisaconcept/21261cd9c7ff04be4447 to your computer and use it in GitHub Desktop.
Save indieisaconcept/21261cd9c7ff04be4447 to your computer and use it in GitHub Desktop.
Mocking node.js dependies
var proxyquire = require('proxyquire').noPreserveCache(),
leche = require('leche'),
sinon = require('sinon'),
npath = require('path'),
_ = require('lodash');
/**
* @ngdoc function
* @name proxy
*
* @description
* Creates a scoped function using passed in base path which permits the loading
* of a module mocking / faking it's required dependencies via proxyquire
*
* @param {string} base Base path to use for when loading modules
*
* @returns {function}
*
*/
module.exports = function proxy (base) {
base = base || module.parent &&
npath.dirname(module.parent.id) || __dirname;
/**
* @ngdoc function
* @name load
*
* @description
* Enhanced wrapper around proxyquire permitting mocks to be generated, using
* leche.
*
* @param {string} subject The module path to load
* @param {object} mocks Object describing the mocks to be applied
*
* @example
*
* subject = prxy('../../../../lib/template-loader/', {
* 'logging' : {
* require: '../logging',
* using: {
* type: 'fake',
* src: '../../../../lib/logging'
* }
* }
* };
*
* @returns {function}
*
*/
return function load (subject, mocks) {
mocks = mocks || {};
var locals = {};
subject = npath.resolve(base, subject);
// create mocks for use when proxying
if (mocks) {
locals = Object.keys(mocks).reduce(function (acc, next) {
if (next === 'sandbox') {
console.log(next);
}
var item = mocks[next],
mock = item.using && item.using.type || 'fake',
src = item.using && item.using.src,
handler,
rtn;
src = _.isString(src) ? npath.resolve(base, src) : src;
rtn = module.exports.create(next, mock, src);
delete module.exports.require.cache[module.exports.require.resolve(src)];
if (rtn) {
acc.proxy[item.require] = rtn;
acc.mocks[next] = rtn;
}
return acc;
}, {
proxy: {},
mocks: {}
});
locals = {
module : module.exports.proxyquire(subject, locals.proxy),
mocks : locals.mocks
};
}
return locals;
};
};
/**
* @ngdoc function
* @name helpers
*
* @description
* Mock helpers
*
*/
module.exports.helpers = {
'fake' : leche.fake,
'create': function (src) {
var result;
result = _.isString(src) ? module.exports.require(src) : src;
result = _.isPlainObject(result) ? Object.keys(result) : result;
result = _.isFunction(result) ?
result : (_.isArray(result) ? leche.create(result) : null);
return result;
},
'stub' : sinon.stub,
'spy' : sinon.spy,
'mock' : sinon.mock
};
module.exports.proxyquire = proxyquire;
module.exports.require = require;
/**
* @ngdoc function
* @name creates
*
* @description
* Creates mocks using pre-defined mock helpers
*
* @param {string} name The mock name
* @param {string} method The type of mock to use
* @param {string|funciton} src The src for the mock
*
* @example
*
* create('sandbox', 'stub', '../../path/to/module');
* create('sandbox', 'stub', function () {});
*
* @returns {object|function} mock
*
*/
module.exports.create = function (name, method, src) {
var result = _.isString(src) ? module.exports.require(src) : src,
handler = module.exports.helpers[method];
if (!handler) {
throw new Error('Unknown mock type used: ' + method);
}
result = _.isFunction(result) ? function () {
throw new Error('Unexpected call to "' + name + '"');
} : handler(result);
return result;
};
@indieisaconcept
Copy link
Author

Using a combination of the following modules behind the scenes.

Example

test = prxy('../../../../lib/template-loader/', {
    'logging' : { require: '../logging', using: { type: 'stub', src: '../../../../lib/logging' } },
    'adapters': { require: './adapters', using: { type: 'fake', src: '../../../../lib/template-loader/adapters' } },
    'sandbox' : { require: './sandbox',  using: { type: 'spy',  src: '../../../../lib/template-loader/sandbox' } }
});

returns: 

{
    module: function () {},         // module required by proxyquire
    mocks {
        'logging' : {},             // created by sinon.stub
        'adapters': function () {}, // created by leche.fake
        'sandbox' : function () {}  // created by sinon.spy 
    }
}

Mocks are passed to proxyquire using require path set in initial mock object.

Known limitations

  • module.export.function which returns a funciton

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