Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

proxyquire

Package Overview
Dependencies
Maintainers
1
Versions
48
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

proxyquire - npm Package Compare versions

Comparing version 0.6.0 to 1.0.0

test/proxyquire-compat.js

7

index.js
'use strict';
var Proxyquire = require('./lib/proxyquire')
, compat = require('./lib/compat');
var Proxyquire = require('./lib/proxyquire');

@@ -10,2 +9,4 @@ // delete this module from the cache to force re-require in order to allow resolving test module via parent.module

module.exports = new Proxyquire(module.parent);
module.exports.compat = function (useGlobal) { return compat(module.parent, useGlobal); };
module.exports.compat = function() {
throw new Error("Proxyquire compat mode has been removed. Please update your code to use the new API or pin the version in your package.json file to ~0.6");
};

@@ -8,2 +8,3 @@ 'use strict';

, is = require('./is')
, assert = require('assert')
;

@@ -118,58 +119,129 @@

// Find the ID (location) of the SUT, relative to the parent
var id = Module._resolveFilename(request, this._parent);
// Find out if any of the passed stubs are global overrides
for (var key in stubs) {
if (stubs[key].hasOwnProperty('@global')) {
this._containsGlobal = true;
}
// Temporarily delete the SUT from the require cache, if it exists.
var cached = Module._cache[id];
if (cached) delete Module._cache[id];
if (stubs[key].hasOwnProperty('@runtimeGlobal')) {
this._containsGlobal = true;
this._containsRuntimeGlobal = true;
}
}
// Override the core require function for the SUT's file extension.
var extname = path.extname(id);
var ext_super = require.extensions[extname];
// Ignore the module cache when return the requested module
return this._withoutCache(this._parent, stubs, request, this._parent.require.bind(this._parent, request));
};
var self = this;
require.extensions[extname] = function ext(module, filename) {
// NOTE: This function is for requiring the SUT
// This replaces a module's require function
Proxyquire.prototype._require = function(module, stubs, path) {
assert(typeof path === 'string', 'path must be a string');
assert(path, 'missing path');
// require_super is the normal require for the SUT.
var require_super = module.require.bind(module);
require_super.extensions = require.extensions;
require_super.extensions[extname] = ext_super;
require_super.main = process.mainModule;
if (stubs.hasOwnProperty(path)) {
var stub = stubs[path];
module.require = function (request) {
// NOTE: This function is for requiring dependencies for the SUT
if (stub.hasOwnProperty('@noCallThru') ? !stub['@noCallThru'] : !this._noCallThru) {
fillMissingKeys(stub, Module._load(path, module));
}
// If the request string isn't stubbed, just do the usual thing.
if (!stubs.hasOwnProperty(request)) return require_super(request);
// We are top level or this stub is marked as global
if (module.parent == this._parent || stub.hasOwnProperty('@global') || stub.hasOwnProperty('@runtimeGlobal')) {
return stub;
}
}
var stub = stubs[request];
// Only ignore the cache if we have global stubs
if (this._containsRuntimeGlobal) {
return this._withoutCache(module, stubs, path, Module._load.bind(Module, path, module));
} else {
return Module._load(path, module);
}
};
if (stub.hasOwnProperty('@noCallThru') ? !stub['@noCallThru'] : !self._noCallThru)
fillMissingKeys(stub, require_super(request));
Proxyquire.prototype._withoutCache = function(module, stubs, path, func) {
// Temporarily disable the cache - either per-module or globally if we have global stubs
var restoreCache = this._disableCache(module, path);
return stub;
};
// Override all require extension handlers
var restoreExtensionHandlers = this._overrideExtensionHandlers(module, stubs);
// Now that we've overridden the SUT's require, we can proceed as usual.
return ext_super(module, filename);
};
try {
return this._parent.require(request);
// Execute the function that needs the module cache disabled
return func();
} finally {
if (self._preserveCache && cached) {
if (cached)
Module._cache[id] = cached;
else
delete Module._cache[id];
// Restore the cache if we are preserving it
if (this._preserveCache) {
restoreCache();
}
if (ext_super)
require.extensions[extname] = ext_super;
}
else
delete require.cache[id];
// Finally restore the original extension handlers
restoreExtensionHandlers();
}
};
Proxyquire.prototype._disableCache = function(module, path) {
if (this._containsGlobal) {
// empty the require cache because if we are stubbing C but requiring A,
// and if A requires B and B requires C, then B and C might be cached already
// and we'll never get the chance to return our stub
return this._disableGlobalCache();
}
// Temporarily delete the SUT from the require cache
return this._disableModuleCache(path, module);
};
Proxyquire.prototype._disableGlobalCache = function() {
var cache = require.cache;
require.cache = Module._cache = {};
// Return a function that will undo what we just did
return function() {
require.cache = Module._cache = cache;
};
};
Proxyquire.prototype._disableModuleCache = function(path, module) {
// Find the ID (location) of the SUT, relative to the parent
var id = Module._resolveFilename(path, module);
var cached = Module._cache[id];
delete Module._cache[id];
// Return a function that will undo what we just did
return function() {
if (cached) {
Module._cache[id] = cached;
}
};
};
Proxyquire.prototype._overrideExtensionHandlers = function(module, stubs) {
var originalExtensions = {};
var self = this;
Object.keys(require.extensions).forEach(function(extension) {
// Store the original so we can restore it later
if (!originalExtensions[extension]) {
originalExtensions[extension] = require.extensions[extension];
}
// Override the default handler for the requested file extension
require.extensions[extension] = function(module, filename) {
// Override the require method for this module
module.require = self._require.bind(self, module, stubs);
return originalExtensions[extension](module, filename);
};
});
// Return a function that will undo what we just did
return function() {
Object.keys(originalExtensions).forEach(function(extension) {
require.extensions[extension] = originalExtensions[extension];
});
};
};
module.exports = Proxyquire;
{
"name": "proxyquire",
"version": "0.6.0",
"version": "1.0.0",
"description": "Proxies nodejs require in order to allow overriding dependencies during testing.",

@@ -32,6 +32,6 @@ "main": "index.js",

"devDependencies": {
"mocha": "1.3.x",
"should": "0.6.x",
"sinon": "~1.4.2"
"mocha": "~1.18",
"should": "~3.3",
"sinon": "~1.9"
}
}

@@ -13,3 +13,3 @@ # proxyquire [![Build Status](https://secure.travis-ci.org/thlorenz/proxyquire.png)](http://travis-ci.org/thlorenz/proxyquire)

- **no changes to your code** are necessary
- **no changes to your code** are necessary
- non overriden methods of a module behave like the original

@@ -26,7 +26,7 @@ - mocking framework agnostic, if it can stub a function then it works with proxyquire

module.exports.extnameAllCaps = function (file) {
module.exports.extnameAllCaps = function (file) {
return path.extname(file).toUpperCase();
};
module.exports.basenameAllCaps = function (file) {
module.exports.basenameAllCaps = function (file) {
return path.basename(file).toUpperCase();

@@ -57,2 +57,4 @@ };

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](http://doctoc.herokuapp.com/)*

@@ -62,11 +64,19 @@

- [API](#api)
- [Preventing call thru to original dependency](#preventing-call-thru-to-original-dependency)
- [Prevent call thru for all future stubs resolved by a proxyquire instance](#prevent-call-thru-for-all-future-stubs-resolved-by-a-proxyquire-instance)
- [Re-enable call thru for all future stubs resolved by a proxyquire instance](#re-enable-call-thru-for-all-future-stubs-resolved-by-a-proxyquire-instance)
- [All together, now](#all-together-now)
- [Forcing proxyquire to reload modules](#forcing-proxyquire-to-reload-modules)
- [Examples](#examples)
- [Preventing call thru to original dependency](#preventing-call-thru-to-original-dependency)
- [Prevent call thru for all future stubs resolved by a proxyquire instance](#prevent-call-thru-for-all-future-stubs-resolved-by-a-proxyquire-instance)
- [Re-enable call thru for all future stubs resolved by a proxyquire instance](#re-enable-call-thru-for-all-future-stubs-resolved-by-a-proxyquire-instance)
- [All together, now](#all-together-now)
- [Forcing proxyquire to reload modules](#forcing-proxyquire-to-reload-modules)
- [Globally override require](#globally-override-require)
- [Caveat](#caveat)
- [Globally override require during module initialization](#globally-override-require-during-module-initialization)
- [Why is proxyquire messing with my `require` cache?](#why-is-proxyquire-messing-with-my-require-cache)
- [Globally override require during module runtime](#globally-override-require-during-module-runtime)
- [Backwards Compatibility for proxyquire v0.3.x](#backwards-compatibility-for-proxyquire-v03x)
- [Examples](#examples)
- [More Examples](#more-examples)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
# Usage

@@ -85,3 +95,3 @@

- **stubs**: key/value pairs of the form `{ modulePath: stub, ... }`
- module paths are relative to the tested module **not** the test file
- module paths are relative to the tested module **not** the test file
- therefore specify it exactly as in the require statement inside the tested file

@@ -138,4 +148,4 @@ - values themselves are key/value pairs of functions/properties and the appropriate override

// for 'path' module they will be made
, path: {
extname: function (file) { ... }
, path: {
extname: function (file) { ... }
, '@noCallThru': false

@@ -146,3 +156,3 @@ }

### All together, now
#### All together, now

@@ -161,6 +171,6 @@ ```javascript

### Forcing proxyquire to reload modules
## 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.
get pulled from the cache the next time.

@@ -199,4 +209,140 @@ For some tests however you need to ensure that the module gets loaded fresh everytime, i.e. if that causes initializing

## Examples
## 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:
- test each module in isolation
- make sure your modules are small enough and do only one thing
- stub out dependencies direclty instead of stubbing something inside your dependencies
- if you are testing `bar` and `bar` calls `foo.read` and `foo.read` calls `fs.readFile` proceed as follows
- **do not** stub out `fs.readFile` globally
- instead stub out `foo` 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.
### Globally override require during module initialization
```javascript
// 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:
```javascript
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.
### 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:
```javascript
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:
```javascript
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.
# 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:**

@@ -250,7 +396,7 @@

var foo = proxyquire('./foo', {
'./bar' : {
toAtm: function (val) { return 0; /* wonder what happens now */ }
'./bar' : {
toAtm: function (val) { return 0; /* wonder what happens now */ }
}
, path : {
extname: function (file) { return 'exterminate the name of ' + file; }
, path : {
extname: function (file) { return 'exterminate the name of ' + file; }
}

@@ -260,13 +406,2 @@ });

# Backwards Compatibility for proxyquire v0.3.x
To upgrade your project from v0.3.x to v0.4.x, a nifty compat function has been included.
Simply do a global find and replace for `require('proxyquire')` and change them to `require('proxyquire').compat()`.
This returns an object that wraps the result of `proxyquire()` that provides exactly the same API as v0.3.x.
If your test scripts relied on the fact that v0.3.x stored `noCallThru` in the module scope, you can use
`require('proxyquire').compat(true)` to use a global compat object, instead.
# More Examples

@@ -281,2 +416,1 @@

- using proxyquire with [Sinon.JS](http://sinonjs.org/): [examples/sinon](https://github.com/thlorenz/proxyquire/tree/master/examples/sinon).

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc