Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
proxyquire
Advanced tools
Proxies nodejs require in order to allow overriding dependencies during testing.
Proxyquire is a powerful tool for stubbing and mocking dependencies in Node.js unit tests. It allows you to override dependencies during testing without modifying the actual code, making it easier to isolate and test individual modules.
Basic Dependency Override
This feature allows you to override a dependency with a mock implementation. In this example, the './dependency' module's 'myFunction' is replaced with a mock function that returns 'mocked value'.
const proxyquire = require('proxyquire');
const myModule = proxyquire('./myModule', {
'./dependency': { myFunction: () => 'mocked value' }
});
console.log(myModule.useDependency()); // Outputs: 'mocked value'
Override Multiple Dependencies
This feature allows you to override multiple dependencies at once. In this example, both './dependency1' and './dependency2' are replaced with mock implementations.
const proxyquire = require('proxyquire');
const myModule = proxyquire('./myModule', {
'./dependency1': { myFunction1: () => 'mocked value 1' },
'./dependency2': { myFunction2: () => 'mocked value 2' }
});
console.log(myModule.useDependencies()); // Outputs: 'mocked value 1 and mocked value 2'
Conditional Overrides
This feature allows you to conditionally override dependencies based on certain conditions, such as environment variables. In this example, the './dependency' module is only mocked if the TEST_ENV environment variable is set.
const proxyquire = require('proxyquire');
const myModule = proxyquire('./myModule', {
'./dependency': process.env.TEST_ENV ? { myFunction: () => 'mocked value' } : require('./dependency')
});
console.log(myModule.useDependency()); // Outputs: 'mocked value' if TEST_ENV is set, otherwise the real value
Sinon is a popular library for creating spies, stubs, and mocks in JavaScript. Unlike Proxyquire, which focuses on overriding dependencies, Sinon provides a more comprehensive set of tools for creating test doubles and verifying interactions.
Rewire is another library that allows you to modify the behavior of modules during testing. It provides a similar functionality to Proxyquire but with a different API. Rewire allows you to access private variables and functions within a module, which can be useful for more fine-grained control.
Mock-require is a simpler alternative to Proxyquire that allows you to mock Node.js modules. It provides a straightforward API for replacing modules with mock implementations, but it lacks some of the advanced features of Proxyquire.
Proxies nodejs's require in order to make overriding dependencies during testing easy while staying totally unobtrusive.
If you want to stub dependencies for your client side modules, try proxyquireify, a proxyquire for browserify v2 or proxyquire-universal to test in both Node and the browser.
foo.js:
var path = require('path');
module.exports.extnameAllCaps = function (file) {
return path.extname(file).toUpperCase();
};
module.exports.basenameAllCaps = function (file) {
return path.basename(file).toUpperCase();
};
foo.test.js:
var proxyquire = require('proxyquire')
, assert = require('assert')
, pathStub = { };
// when no overrides are specified, path.extname behaves normally
var foo = proxyquire('./foo', { 'path': pathStub });
assert.strictEqual(foo.extnameAllCaps('file.txt'), '.TXT');
// override path.extname
pathStub.extname = function (file) { return 'Exterminate, exterminate the ' + file; };
// path.extname now behaves as we told it to
assert.strictEqual(foo.extnameAllCaps('file.txt'), 'EXTERMINATE, EXTERMINATE THE FILE.TXT');
// path.basename and all other path module methods still function as before
assert.strictEqual(foo.basenameAllCaps('/a/b/file.txt'), 'FILE.TXT');
You can also replace functions directly:
get.js:
var get = require('simple-get');
var assert = require('assert');
module.exports = function fetch (callback) {
get('https://api/users', callback);
};
get.test.js:
var proxyquire = require('proxyquire').noCallThru();
var assert = require('assert');
var fetch = proxyquire('./get', {
'simple-get': function (url, callback) {
process.nextTick(function () {
callback(null, { statusCode: 200 })
})
}
});
fetch(function (err, res) {
assert(res.statusCode, 200)
});
Table of Contents generated with DocToc
Two simple steps to override require in your tests:
var proxyquire = require('proxyquire');
to top level of your test fileproxyquire(...)
the module you want to test and pass along stubs for modules you want to overrideproxyquire({string} request, {Object} stubs)
../lib/foo
{ modulePath: stub, ... }
By default proxyquire calls the function defined on the original dependency whenever it is not found on the stub.
If you prefer a more strict behavior you can prevent callThru on a per module or contextual basis.
If callThru is disabled, you can stub out modules that don't even exist on the machine that your tests are running on. While I wouldn't recommend this in general, I have seen cases where it is legitimately useful (e.g., when requiring global environment configs in json format that may not be available on all machines).
Prevent call thru on path stub:
var foo = proxyquire('./foo', {
path: {
extname: function (file) { ... }
, '@noCallThru': true
}
});
// all stubs resolved by proxyquireStrict will not call through by default
var proxyquireStrict = require('proxyquire').noCallThru();
// all stubs resolved by proxyquireNonStrict will call through by default
var proxyquireNonStrict = require('proxyquire');
proxyquire.callThru();
Call thru configurations per module override callThru()
:
Passing @noCallThru: false
when configuring modules will override noCallThru()
:
var foo = proxyquire
.noCallThru()
.load('./foo', {
// no calls to original './bar' methods will be made
'./bar' : { toAtm: function (val) { ... } }
// for 'path' module they will be made
, path: {
extname: function (file) { ... }
, '@noCallThru': false
}
});
var proxyquire = require('proxyquire').noCallThru();
// all methods for foo's dependencies will have to be stubbed out since proxyquire will not call through
var foo = proxyquire('./foo', stubs);
proxyquire.callThru();
// only some methods for foo's dependencies will have to be stubbed out here since proxyquire will now call through
var foo2 = proxyquire('./foo', stubs);
Some libraries may behave differently in the presence or absence of a package, for example:
var cluster;
try {
cluster = require('cluster');
} catch(e) {
// cluster module is not present.
cluster = null
}
if (cluster) {
// Then provide some functionality for a cluster-aware version of Node.js
} else {
// and some alternative for a cluster-unaware version.
}
To exercise the second branch of the if
statement, you can make proxyquire pretend the package isn't present by
setting the stub for it to null
. This works even if a cluster
module is actually present.
var foo = proxyquire('./foo', { cluster: null });
In most situations it is fine to have proxyquire behave exactly like nodejs require
, i.e. modules that are loaded once
get pulled from the cache the next time.
For some tests however you need to ensure that the module gets loaded fresh everytime, i.e. if that causes initializing some dependency or some module state.
For this purpose proxyquire exposes the noPreserveCache
function.
// ensure we don't get any module from the cache, but to load it fresh every time
var proxyquire = require('proxyquire').noPreserveCache();
var foo1 = proxyquire('./foo', stubs);
var foo2 = proxyquire('./foo', stubs);
var foo3 = require('./foo');
// foo1, foo2 and foo3 are different instances of the same module
assert.notStrictEqual(foo1, foo2);
assert.notStrictEqual(foo1, foo3);
proxyquire.preserveCache
allows you to restore the behavior to match nodejs's require
again.
proxyquire.preserveCache();
var foo1 = proxyquire('./foo', stubs);
var foo2 = proxyquire('./foo', stubs);
var foo3 = require('./foo');
// foo1, foo2 and foo3 are the same instance
assert.strictEqual(foo1, foo2);
assert.strictEqual(foo1, foo3);
Use the @global
property to override every require
of a module, even transitively.
You should think very hard about alternatives before using this feature. Why, because it's intrusive and as you'll see if you read on it changes the default behavior of module initialization which means that code runs differently during testing than it does normally.
Additionally it makes it harder to reason about how your tests work.
Yeah, we are mocking
fs
three levels down inbar
, so that's why we have to set it up when testingfoo
WAAAT???
If you write proper unit tests you should never have a need for this. So here are some techniques to consider:
bar
and bar
calls foo.read
and foo.read
calls fs.readFile
proceed as follows
fs.readFile
globallyfoo
so you can control what foo.read
returns without ever even hitting fs
OK, made it past the warnings and still feel like you need this? Read on then but you are on your own now, this is as far as I'll go ;)
Watch out for more warnings below.
// foo.js
var bar = require('./bar');
module.exports = function() {
bar();
}
// bar.js
var baz = require('./baz');
module.exports = function() {
baz.method();
}
// baz.js
module.exports = {
method: function() {
console.info('hello');
}
}
// test.js
var stubs = {
'./baz': {
method: function(val) {
console.info('goodbye');
},
'@global': true
}
};
var proxyquire = require('proxyquire');
var foo = proxyquire('./foo', stubs);
foo(); // 'goodbye' is printed to stdout
Be aware that when using global overrides any module initialization code will be re-executed for each require.
This is not normally the case since node.js caches the return value of require
, however to make global overrides work ,
proxyquire
bypasses the module cache. This may cause unexpected behaviour if a module's initialization causes side effects.
As an example consider this module which opens a file during its initialization:
var fs = require('fs')
, C = require('C');
// will get executed twice
var file = fs.openSync('/tmp/foo.txt', 'w');
module.exports = function() {
return new C(file);
};
The file at /tmp/foo.txt
could be created and/or truncated more than once.
require
cache?Say you have a module, C, that you wish to stub. You require module A which contains require('B')
. Module B in turn
contains require('C')
. If module B has already been required elsewhere then when module A receives the cached version
of module B and proxyquire would have no opportunity to inject the stub for C.
Therefore when using the @global
flag, proxyquire
will bypass the require
cache.
Say you have a module that looks like this:
module.exports = function() {
var d = require('d');
d.method();
};
The invocation of require('d')
will happen at runtime and not when the containing module is requested via require
.
If you want to globally override d
above, use the @runtimeGlobal
property:
var stubs = {
'd': {
method: function(val) {
console.info('hello world');
},
'@runtimeGlobal': true
}
};
This will cause module setup code to be re-excuted just like @global
, but with the difference that it will happen
every time the module is requested via require
at runtime as no module will ever be cached.
This can cause subtle bugs so if you can guarantee that your modules will not vary their require
behaviour at runtime,
use @global
instead.
Even if you want to override a module that exports a function directly, you can still set special properties like @global
. You can use a named function or assign your stub function to a variable to add properties:
foo['@global'] = true;
function foo () {}
proxyquire('./bar', {
foo: foo
});
And if your stub is in a separate module where module.exports = foo
:
var foostub = require('../stubs/foostub');
foostub['@global'] = true;
proxyquire('bar', {
foo: foostub
});
Compatibility mode with proxyquire v0.3.x has been removed.
You should update your code to use the newer API but if you can't, pin the version of proxyquire in your package.json file to ~0.6 in order to continue using the older style.
We are testing foo which depends on bar:
// bar.js module
module.exports = {
toAtm: function (val) { return 0.986923267 * val; }
};
// foo.js module
// requires bar which we will stub out in tests
var bar = require('./bar');
[ ... ]
Tests:
// foo-test.js module which is one folder below foo.js (e.g., in ./tests/)
/*
* Option a) Resolve and override in one step:
*/
var foo = proxyquire('../foo', {
'./bar': { toAtm: function (val) { return 0; /* wonder what happens now */ } }
});
// [ .. run some tests .. ]
/*
* Option b) Resolve with empty stub and add overrides later
*/
var barStub = { };
var foo = proxyquire('../foo', { './bar': barStub });
// Add override
barStub.toAtm = function (val) { return 0; /* wonder what happens now */ };
[ .. run some tests .. ]
// Change override
barStub.toAtm = function (val) { return -1 * val; /* or now */ };
[ .. run some tests .. ]
// Resolve foo and override multiple of its dependencies in one step - oh my!
var foo = proxyquire('./foo', {
'./bar' : {
toAtm: function (val) { return 0; /* wonder what happens now */ }
}
, path : {
extname: function (file) { return 'exterminate the name of ' + file; }
}
});
For more examples look inside the examples folder or look through the tests
Specific Examples:
FAQs
Proxies nodejs require in order to allow overriding dependencies during testing.
The npm package proxyquire receives a total of 666,041 weekly downloads. As such, proxyquire popularity was classified as popular.
We found that proxyquire demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.