From 77f14567604fce7ef00b16acbe4c10f5ce9581e3 Mon Sep 17 00:00:00 2001 From: g-i-o- Date: Sun, 23 Aug 2015 12:57:53 -0400 Subject: [PATCH] adding intersection of require calls --- README.md | 34 +++++++++++++++++++++++++++++ lib/index.js | 11 +++++++--- lib/rewire.js | 22 ++++++++++++++++++- test/testModules/sharedTestCases.js | 29 ++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2e1a89a..d22a3e3 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,40 @@ myModule.__with__({ // port is still 3000 here because the promise hasn't been resolved yet ``` +### Intercepting require calls +You can intercept calls to `require` by giving `rewire` a second argument. + +```javascript +var rewire = require("rewire"); + +var myModule = rewire("../lib/myModule.js", { + 'fs' : fsMock, + './otherModule.js' : { otherFunction: function(){} } +}); +``` + +Calls to `require` using paths found in the given object will be intercepted, and the relevant +mock object will be returned. + +You can also use a function to resolve the intercepted call. + + +```javascript +var rewire = require("rewire"); + +var myModule = rewire("../lib/myModule.js", function (path){ + if(/otherModule/.test(path)){ + return { otherFunction: function(){} }; + } else { + return undefined; + } +}); +``` + + +If the function's return value will be used as the mock object, if it is not `undefined`. Otherwise the original `require` function is called. + + ### Limitations **Variables inside functions**
diff --git a/lib/index.js b/lib/index.js index 11229ff..2a6c563 100644 --- a/lib/index.js +++ b/lib/index.js @@ -5,12 +5,17 @@ var rewireModule = require("./rewire.js"); * call myModule.__set__(name, value) and myModule.__get__(name) to manipulate private variables. * * @param {!String} filename Path to the module that shall be rewired. Use it exactly like require(). + * @param {Object|Function} require_mocks Mock to be returned by require() within the module. + * If it is a function, then it will be called with the path of the required module as + * argument. The `this` context of the function will be the module. If the function + * returns undefined, then the old require function is used, else, then it is taken as + * the require function's return value. * @return {*} the rewired module */ -function rewire(filename) { - return rewireModule(module.parent, filename); +function rewire(filename, requireMocks) { + return rewireModule(module.parent, filename, requireMocks); } module.exports = rewire; -delete require.cache[__filename]; // deleting self from module cache so the parent module is always up to date \ No newline at end of file +delete require.cache[__filename]; // deleting self from module cache so the parent module is always up to date diff --git a/lib/rewire.js b/lib/rewire.js index c52bbeb..e4898ef 100644 --- a/lib/rewire.js +++ b/lib/rewire.js @@ -8,7 +8,7 @@ var Module = require("module"), /** * Does actual rewiring the module. For further documentation @see index.js */ -function internalRewire(parentModulePath, targetPath) { +function internalRewire(parentModulePath, targetPath, requireMocks) { var targetModule, prelude, appendix, @@ -36,6 +36,26 @@ function internalRewire(parentModulePath, targetPath) { // We prepend a list of all globals declared with var so they can be overridden (without changing original globals) prelude = getImportGlobalsSrc(); + // Compute the required mocks injection code, and inject the prelude; + if(requireMocks){ + if(requireMocks instanceof Function){ + targetModule.__mock_require__ = requireMocks; + } else { + targetModule.__mock_require__ = function(path){ + return requireMocks && requireMocks[path]; + }; + } + + prelude += ( + 'var require = (function(oldRequire){' + + 'return function(path){' + + 'var mock = module.__mock_require__(path);' + + 'return mock === undefined ? oldRequire(path) : mock;' + + '}' + + '})(require);' + ); + } + // Wrap module src inside IIFE so that function declarations do not clash with global variables // @see https://github.com/jhnns/rewire/issues/56 prelude += "(function () { "; diff --git a/test/testModules/sharedTestCases.js b/test/testModules/sharedTestCases.js index 7048ba8..bf4b464 100644 --- a/test/testModules/sharedTestCases.js +++ b/test/testModules/sharedTestCases.js @@ -92,6 +92,35 @@ describe("rewire " + (typeof testEnv === "undefined"? "(node)": "(" + testEnv + rewire("./moduleB.js").checkSomeGlobals(); }); + it("should mock targeted require calls if given an object", function () { + var dummySomeOtherModule = {}; + expect(rewire("./moduleA.js", { + './someOtherModule.js' : dummySomeOtherModule + }).someOtherModule).to.equal(dummySomeOtherModule); + + var dummyFs = {readFileSync:function(){}}; + expect(rewire("./module.coffee", { + 'fs' : dummyFs + }).__get__('fs')).to.equal(dummyFs); + }); + + it("should mock targeted require calls if given a function", function () { + var dummySomeOtherModule = {}; + expect(rewire("./moduleA.js", function(path){ + return /someOtherModule.js/.test(path) && dummySomeOtherModule; + }).someOtherModule).to.equal(dummySomeOtherModule); + + var dummyFs = {readFileSync:function(){}}; + expect(rewire("./module.coffee", function (path) { + return path == 'fs' && dummyFs; + }).__get__('fs')).to.equal(dummyFs); + + var realFs = require('fs'); + expect(rewire("./module.coffee", function (path) { + return undefined; + }).__get__('fs')).to.equal(realFs); + }); + // This is just an integration test for the __set__ method // You can find a full test for __set__ under /test/__set__.test.js it("should provide a working __set__ method", function () {