Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ fetch(function (err, res) {
- [All together, now](#all-together-now)
- [Using proxyquire to simulate the absence of Modules](#using-proxyquire-to-simulate-the-absence-of-modules)
- [Forcing proxyquire to reload modules](#forcing-proxyquire-to-reload-modules)
- [Require all modules to be registered](#require-all-modules-to-be-registered)
- [Globally override require](#globally-override-require)
- [Caveat](#caveat)
- [Globally override require during module initialization](#globally-override-require-during-module-initialization)
Expand Down Expand Up @@ -262,6 +263,29 @@ assert.equal(foo1, foo2);
assert.equal(foo1, foo3);
```

## Require all modules to be registered

Proxyquire also gives you the ability to require that all modules have a registered stub. This is useful to ensure that all module dependencies are declared in your tests and can avoid pitfalls such as including modules that:
1. run in the background
2. contact network dependencies
3. contain business logic

While it makes sense not to mock certain modules you still may want these same modules to be declared and registered as a dependency in your test which is an explicit way to indicate that they are ok to use in your test without being mocked.

For the purpose of requiring modules to have registered stubs, proxyquire exposes the `requireStubs` function.

```js
// ensure modules have registered stubs, otherwise cause proxyquire to fail to load
var proxyquire = require('proxyquire').requireStubs().noCallThru();

// loads fine
var foo1 = proxyquire('./foo', { path: require('path') });

// throws an error since module "path" has no registered stub
var foo2 = proxyquire('./foo', {});
```

`requireStubs` is often used in conjunction with `noCallThru` but the two can be used separately. Modules loaded to support call through behavior can load their dependent modules without stubs being registered.

## Globally override require

Expand Down
26 changes: 26 additions & 0 deletions lib/proxyquire.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ function Proxyquire (parent) {

this._parent = parent
this._preserveCache = true
this._requireStubs = false
this._moduleRequiringStubs = null

Object.keys(proto)
.forEach(function (key) {
Expand Down Expand Up @@ -96,6 +98,19 @@ Proxyquire.prototype.preserveCache = function () {
return this.fn
}

/**
* Requires all modules to have a stub registered with proxyquire
*
* @name requireStubs
* @function
* @private
* @return {object} The proxyquire function to allow chaining
*/
Proxyquire.prototype.requireStubs = function () {
this._requireStubs = true
return this.fn
}

/**
* Loads a module using the given stubs instead of their normally resolved required modules.
* @param request The requirable module path to load.
Expand All @@ -105,6 +120,13 @@ Proxyquire.prototype.preserveCache = function () {
Proxyquire.prototype.load = function (request, stubs) {
validateArguments(request, stubs)

if (this._requireStubs) {
var modulePath = Module._resolveFilename(request, this._parent)
this._moduleRequiringStubs = modulePath
} else {
this._moduleRequiringStubs = null
}

// Find out if any of the passed stubs are global overrides
for (var key in stubs) {
var stub = stubs[key]
Expand Down Expand Up @@ -186,6 +208,10 @@ Proxyquire.prototype._require = function (module, stubs, path) {
}
}

if (this._moduleRequiringStubs === module.filename) {
throw new ProxyquireError('Module at path "' + path + '" does not have a registered stub with proxyquire')
}

// Only ignore the cache if we have global stubs
if (this._containsRuntimeGlobal) {
return this._withoutCache(module, stubs, path, Module._load.bind(Module, path, module))
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "proxyquire",
"version": "2.1.0",
"version": "2.2.0",
"description": "Proxies nodejs require in order to allow overriding dependencies during testing.",
"main": "index.js",
"scripts": {
Expand Down
47 changes: 47 additions & 0 deletions test/proxyquire-cache.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict'

var assert = require('assert')
var ProxyError = require('../lib/proxyquire-error')

describe('Proxyquire', function () {
describe('load()', function () {
Expand Down Expand Up @@ -115,4 +116,50 @@ describe('Proxyquire', function () {
})
})
})

describe('requireStubs()', function () {
it('returns a reference to itself, so it can be chained', function () {
var proxyquire = require('..')
assert.equal(proxyquire.requireStubs(), proxyquire)
})

it('throws an error when a require statement without a registered stub is loaded', () => {
var proxyquire = require('..')
proxyquire.requireStubs()

assert.throws(function () {
proxyquire.load('./samples/require-stubs/dep2', {})
}, function (err) {
return (err instanceof ProxyError) &&
err.message === 'Module at path "./dep3" does not have a registered stub with proxyquire'
})

assert.doesNotThrow(function () {
proxyquire.load('./samples/require-stubs/dep2', { './dep3': {} })
}, 'Unexpected error when loading a module that has a registered stub')
})

it('allows call through modules to load their dependencies without registered stubs', function () {
var proxyquire = require('..')
proxyquire.requireStubs()

// Default call through behavior still works
try {
var dep1 = proxyquire.load('./samples/require-stubs/dep1', {
'./dep2': {}
})

// Dependency Chain:
// - dependency 1 requires depencency 2
// - dependency 2 requires dependency 3
//
// Notes:
// because call through behavior is allowed dependency 2 is loaded which then loads dependency 3
// even though no stub was registered for dependency 3 ('./dep3')
assert.equal(dep1.dep2.dep3.name, 'dep3')
} catch (err) {
assert.fail(err)
}
})
})
})
4 changes: 4 additions & 0 deletions test/samples/require-stubs/dep1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
name: 'dep1',
dep2: require('./dep2')
}
4 changes: 4 additions & 0 deletions test/samples/require-stubs/dep2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
name: 'dep2',
dep3: require('./dep3')
}
3 changes: 3 additions & 0 deletions test/samples/require-stubs/dep3.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
name: 'dep3'
}