peerlibrary/proxyquire

Name: proxyquire

Owner: PeerLibrary

Description: Proxies nodejs require in order to allow overriding dependencies during testing.

Created: 2015-03-08 18:30:03.0

Updated: 2015-03-08 18:30:03.0

Pushed: 2015-03-04 17:15:52.0

Homepage: null

Size: 807

Language: JavaScript

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

proxyquire Build Status

NPM

Proxies nodejs's require in order to make overriding dependencies during testing easy while staying totally unobstrusive.

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.

Features

Example

foo.js:

path = require('path');

le.exports.extnameAllCaps = function (file) {
turn path.extname(file).toUpperCase();


le.exports.basenameAllCaps = function (file) {
turn path.basename(file).toUpperCase();

foo.test.js:

proxyquire =  require('proxyquire')
assert     =  require('assert')
pathStub   =  { };

hen no overrides are specified, path.extname behaves normally
foo = proxyquire('./foo', { 'path': pathStub });
rt.equal(foo.extnameAllCaps('file.txt'), '.TXT');

verride path.extname
Stub.extname = function (file) { return 'Exterminate, exterminate the ' + file; };

ath.extname now behaves as we told it to
rt.equal(foo.extnameAllCaps('file.txt'), 'EXTERMINATE, EXTERMINATE THE FILE.TXT');

ath.basename and all other path module methods still function as before
rt.equal(foo.basenameAllCaps('/a/b/file.txt'), 'FILE.TXT');

Table of Contents generated with DocToc

Usage

Two simple steps to override require in your tests:

API

proxyquire({string} request, {Object} stubs)

Preventing call thru to original dependency

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:

foo = proxyquire('./foo', {
th: {
  extname: function (file) { ... }
, '@noCallThru': true


Prevent call thru for all future stubs resolved by a proxyquire instance
ll stubs resolved by proxyquireStrict will not call through by default
proxyquireStrict = require('proxyquire').noCallThru();

ll stubs resolved by proxyquireNonStrict will call through by default
proxyquireNonStrict = require('proxyquire');
Re-enable call thru for all future stubs resolved by a proxyquire instance
yquire.callThru();

Call thru config per module wins:

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
    }
});
All together, now
proxyquire = require('proxyquire').noCallThru();

ll methods for foo's dependencies will have to be stubbed out since proxyquire will not call through
foo = proxyquire('./foo', stubs);

yquire.callThru();

nly some methods for foo's dependencies will have to be stubbed out here since proxyquire will now call through
foo2 = proxyquire('./foo', stubs);
Using proxyquire to simulate the absence of Modules

Some libraries may behave differently in the presence of absence of a package, for example:

cluster;
{
uster = require('cluster');
tch(e) {
 cluster module is not present.
uster = null

cluster) {
 Then provide some functionality for a cluster-aware version of Node.js
se {
 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.

foo = proxyquire('./foo', { cluster: null });
Forcing proxyquire to reload modules

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.

nsure we don't get any module from the cache, but to load it fresh every time
proxyquire = require('proxyquire').noPreserveCache();

foo1 = proxyquire('./foo', stubs);
foo2 = proxyquire('./foo', stubs);
foo3 = require('./foo');

oo1, foo2 and foo3 are different instances of the same module
rt.notEqual(foo1, foo2);
rt.notEqual(foo1, foo3);

require.preserveCache allows you to restore the behavior to match nodejs's require again.

yquire.preserveCache();

foo1 = proxyquire('./foo', stubs);
foo2 = proxyquire('./foo', stubs);
foo3 = require('./foo');

oo1, foo2 and foo3 are the same instance
t.equal(foo1, foo2);
t.equal(foo1, foo3);
Globally override require

Use the @global property to override every require of a module, even transitively.

Caveat

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 in bar, so that's why we have to set it up when testing foo

WAAAT???

If you write proper unit tests you should never have a need for this. So here are some techniques to consider:

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.

Globally override require during module initialization
oo.js
bar = require('bar');

le.exports = function() {
r();


ar.js
baz = require('baz');

le.exports = function() {
z.method();


az.js
le.exports = {
thod: function() {
console.info('hello');



est.js
stubs = {
az': {
method: function(val) {
  console.info('goodbye');
},
'@global': true



proxyquire = require('proxyquire');

foo = proxyquire('foo', stubs);
);  // '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:

fs = require('fs')
C = require('C');

ill get executed twice
file = fs.openSync('/tmp/foo.txt', 'w');

le.exports = function() {
turn new C(file);

The file at /tmp/foo.txt could be created and/or truncated more than once.

Why is proxyquire messing with my 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.

Globally override require during module runtime

Say you have a module that looks like this:

le.exports = function() {
r d = require('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:

stubs = {
': {
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.

Backwards Compatibility for proxyquire v0.3.x

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.

Examples

We are testing foo which depends on bar:

ar.js module
le.exports = {
toAtm: function (val) { return  0.986923267 * val; }


oo.js module
equires bar which we will stub out in tests
bar = require('./bar');
. ]

Tests:

oo-test.js module which is one folder below foo.js (e.g., in ./tests/)


 Option a) Resolve and override in one step:

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

barStub = { };

foo =  proxyquire('../foo', { './bar': barStub });

dd override
tub.toAtm = function (val) { return 0; /* wonder what happens now */ };

 run some tests .. ]

hange override
tub.toAtm = function (val) { return -1 * val; /* or now */ };

 run some tests .. ]

esolve foo and override multiple of its dependencies in one step - oh my!
foo = proxyquire('./foo', {
'./bar' : {
  toAtm: function (val) { return 0; /* wonder what happens now */ }
}
path    : {
  extname: function (file) { return 'exterminate the name of ' + file; }
}

More Examples

For more examples look inside the examples folder or look through the tests

Specific Examples:


This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.