├── .gitignore ├── .jshintrc ├── .travis.yml ├── LICENSE ├── README.md ├── examples ├── api │ ├── api-test.js │ └── samples │ │ ├── bar.js │ │ ├── foo.js │ │ └── stats.js ├── async │ ├── foo-tests.js │ └── foo.js ├── example-utils.js ├── simple │ ├── foo.inlineoverride.test.js │ ├── foo.js │ └── foo.test.js └── sinon │ ├── foo-tests.js │ └── foo.js ├── index.js ├── lib ├── is.js ├── proxyquire-error.js └── proxyquire.js ├── package.json ├── tea.yaml └── test ├── mocha.opts ├── proxyquire-api.js ├── proxyquire-argumentvalidation.js ├── proxyquire-cache.js ├── proxyquire-compat.js ├── proxyquire-extensions.js ├── proxyquire-global.js ├── proxyquire-independence.js ├── proxyquire-non-object.js ├── proxyquire-notexisting.js ├── proxyquire-relative-paths.js ├── proxyquire-remove.js ├── proxyquire-sub-dependencies.js ├── proxyquire.js └── samples ├── bar.js ├── boof.js ├── cache ├── bar.js └── foo.js ├── extensions.js ├── foo-singleton.js ├── foo.js ├── fooarray.js ├── foobool.js ├── foonum.js ├── global ├── bar.js ├── baz.js ├── foo-deferred.js └── foo.js ├── no-call-thru-test ├── index.js └── required.js ├── notexisting ├── foo-relative.js └── foo.js ├── relative-paths ├── a │ ├── index.js │ └── util.js └── b │ ├── index.js │ └── util.js ├── stats.js └── sub-dependencies ├── bar.js ├── baz.js └── foo.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.user.js 2 | 3 | lib-cov 4 | *.seed 5 | *.log 6 | *.csv 7 | *.dat 8 | *.out 9 | *.pid 10 | *.gz 11 | 12 | pids 13 | logs 14 | results 15 | 16 | node_modules 17 | npm-debug.log 18 | 19 | spikes 20 | 21 | *.idea 22 | *.iml 23 | .DS_Store 24 | coverage 25 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "laxcomma" : true 3 | , "sub" : true 4 | , "onecase" : true 5 | , "node" : true 6 | } 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '4' 4 | - '6' 5 | - '8' 6 | env: 7 | - CXX=g++-4.8 8 | addons: 9 | apt: 10 | sources: 11 | - ubuntu-toolchain-r-test 12 | packages: 13 | - g++-4.8 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2013 Thorsten Lorenz. 2 | All rights reserved. 3 | 4 | Permission is hereby granted, free of charge, to any person 5 | obtaining a copy of this software and associated documentation 6 | files (the "Software"), to deal in the Software without 7 | restriction, including without limitation the rights to use, 8 | copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the 10 | Software is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 20 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | OTHER DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # proxyquire [![Build Status](https://secure.travis-ci.org/thlorenz/proxyquire.svg?branch=master)](http://travis-ci.org/thlorenz/proxyquire) 2 | 3 | Proxies nodejs's require in order to make overriding dependencies during testing easy while staying **totally unobtrusive**. 4 | 5 | If you want to stub dependencies for your client side modules, try 6 | [proxyquireify](https://github.com/thlorenz/proxyquireify), a proxyquire for [browserify 7 | v2](https://github.com/substack/browserify) or [proxyquire-universal](https://github.com/bendrucker/proxyquire-universal) 8 | to test in both Node and the browser. 9 | 10 | # Features 11 | 12 | - **no changes to your code** are necessary 13 | - non overridden methods of a module behave like the original 14 | - mocking framework agnostic, if it can stub a function then it works with proxyquire 15 | - "use strict" compliant 16 | 17 | # Example 18 | 19 | **foo.js:** 20 | 21 | ```javascript 22 | var path = require('path'); 23 | 24 | module.exports.extnameAllCaps = function (file) { 25 | return path.extname(file).toUpperCase(); 26 | }; 27 | 28 | module.exports.basenameAllCaps = function (file) { 29 | return path.basename(file).toUpperCase(); 30 | }; 31 | ``` 32 | 33 | **foo.test.js:** 34 | 35 | ```javascript 36 | var proxyquire = require('proxyquire') 37 | , assert = require('assert') 38 | , pathStub = { }; 39 | 40 | // when no overrides are specified, path.extname behaves normally 41 | var foo = proxyquire('./foo', { 'path': pathStub }); 42 | assert.strictEqual(foo.extnameAllCaps('file.txt'), '.TXT'); 43 | 44 | // override path.extname 45 | pathStub.extname = function (file) { return 'Exterminate, exterminate the ' + file; }; 46 | 47 | // path.extname now behaves as we told it to 48 | assert.strictEqual(foo.extnameAllCaps('file.txt'), 'EXTERMINATE, EXTERMINATE THE FILE.TXT'); 49 | 50 | // path.basename and all other path module methods still function as before 51 | assert.strictEqual(foo.basenameAllCaps('/a/b/file.txt'), 'FILE.TXT'); 52 | ``` 53 | 54 | You can also replace functions directly: 55 | 56 | **get.js:** 57 | 58 | ```js 59 | var get = require('simple-get'); 60 | var assert = require('assert'); 61 | 62 | module.exports = function fetch (callback) { 63 | get('https://api/users', callback); 64 | }; 65 | ``` 66 | 67 | **get.test.js:** 68 | 69 | ```js 70 | var proxyquire = require('proxyquire').noCallThru(); 71 | var assert = require('assert'); 72 | 73 | var fetch = proxyquire('./get', { 74 | 'simple-get': function (url, callback) { 75 | process.nextTick(function () { 76 | callback(null, { statusCode: 200 }) 77 | }) 78 | } 79 | }); 80 | 81 | fetch(function (err, res) { 82 | assert(res.statusCode, 200) 83 | }); 84 | ``` 85 | 86 | 87 | 88 | 89 | **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* 90 | 91 | - [Usage](#usage) 92 | - [API](#api) 93 | - [Preventing call thru to original dependency](#preventing-call-thru-to-original-dependency) 94 | - [Prevent call thru for all future stubs resolved by a proxyquire instance](#prevent-call-thru-for-all-future-stubs-resolved-by-a-proxyquire-instance) 95 | - [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) 96 | - [All together, now](#all-together-now) 97 | - [Using proxyquire to simulate the absence of Modules](#using-proxyquire-to-simulate-the-absence-of-modules) 98 | - [Forcing proxyquire to reload modules](#forcing-proxyquire-to-reload-modules) 99 | - [Globally override require](#globally-override-require) 100 | - [Caveat](#caveat) 101 | - [Globally override require during module initialization](#globally-override-require-during-module-initialization) 102 | - [Why is proxyquire messing with my `require` cache?](#why-is-proxyquire-messing-with-my-require-cache) 103 | - [Globally override require during module runtime](#globally-override-require-during-module-runtime) 104 | - [Configuring proxyquire by setting stub properties](#configuring-proxyquire-by-setting-stub-properties) 105 | - [Backwards Compatibility for proxyquire v0.3.x](#backwards-compatibility-for-proxyquire-v03x) 106 | - [Examples](#examples) 107 | - [More Examples](#more-examples) 108 | 109 | 110 | 111 | 112 | # Usage 113 | 114 | Two simple steps to override require in your tests: 115 | 116 | - add `var proxyquire = require('proxyquire');` to top level of your test file 117 | - `proxyquire(...)` the module you want to test and pass along stubs for modules you want to override 118 | 119 | # API 120 | 121 | ***proxyquire({string} request, {Object} stubs)*** 122 | 123 | - **request**: path to the module to be tested e.g., `../lib/foo` 124 | - **stubs**: key/value pairs of the form `{ modulePath: stub, ... }` 125 | - module paths are relative to the tested module **not** the test file 126 | - therefore specify it exactly as in the require statement inside the tested file 127 | - values themselves are key/value pairs of functions/properties and the appropriate override 128 | 129 | ## Preventing call thru to original dependency 130 | 131 | By default proxyquire calls the function defined on the *original* dependency whenever it is not found on the stub. 132 | 133 | If you prefer a more strict behavior you can prevent *callThru* on a per module or contextual basis. If your stub is a class or class instance rather than a plain object, you should disable *callThru* to ensure that it is passed through with the correct prototype. 134 | 135 | ```js 136 | class MockClass { 137 | static '@noCallThru' = true; 138 | } 139 | 140 | var foo = proxyquire('./foo', { 141 | './my-class': MockClass 142 | }); 143 | ``` 144 | 145 | ```js 146 | class MockClass { 147 | get '@noCallThru'() { 148 | return true; 149 | } 150 | } 151 | 152 | var foo = proxyquire('./foo', { 153 | './my-class-instance': new MockClass() 154 | }); 155 | ``` 156 | 157 | If *callThru* is disabled, you can stub out modules that don't even exist on the machine that your tests are running on. 158 | While I wouldn't recommend this in general, I have seen cases where it is legitimately useful (e.g., when requiring 159 | global environment configs in json format that may not be available on all machines). 160 | 161 | **Prevent call thru on path stub:** 162 | 163 | ```javascript 164 | var foo = proxyquire('./foo', { 165 | path: { 166 | extname: function (file) { ... } 167 | , '@noCallThru': true 168 | } 169 | }); 170 | ``` 171 | 172 | ### Prevent call thru for all future stubs resolved by a proxyquire instance 173 | 174 | ```javascript 175 | // all stubs resolved by proxyquireStrict will not call through by default 176 | var proxyquireStrict = require('proxyquire').noCallThru(); 177 | 178 | // all stubs resolved by proxyquireNonStrict will call through by default 179 | var proxyquireNonStrict = require('proxyquire'); 180 | ``` 181 | 182 | ### Re-enable call thru for all future stubs resolved by a proxyquire instance 183 | 184 | ```javascript 185 | proxyquire.callThru(); 186 | ``` 187 | 188 | **Call thru configurations per module override `callThru()`:** 189 | 190 | Passing `@noCallThru: false` when configuring modules will override `noCallThru()`: 191 | 192 | ```javascript 193 | var foo = proxyquire 194 | .noCallThru() 195 | .load('./foo', { 196 | 197 | // no calls to original './bar' methods will be made 198 | './bar' : { toAtm: function (val) { ... } } 199 | 200 | // for 'path' module they will be made 201 | , path: { 202 | extname: function (file) { ... } 203 | , '@noCallThru': false 204 | } 205 | }); 206 | ``` 207 | 208 | #### All together, now 209 | 210 | ```javascript 211 | var proxyquire = require('proxyquire').noCallThru(); 212 | 213 | // all methods for foo's dependencies will have to be stubbed out since proxyquire will not call through 214 | var foo = proxyquire('./foo', stubs); 215 | 216 | proxyquire.callThru(); 217 | 218 | // only some methods for foo's dependencies will have to be stubbed out here since proxyquire will now call through 219 | var foo2 = proxyquire('./foo', stubs); 220 | ``` 221 | 222 | ## Using proxyquire to simulate the absence of Modules 223 | 224 | Some libraries may behave differently in the presence or absence of a 225 | package, for example: 226 | 227 | ```javascript 228 | var cluster; 229 | try { 230 | cluster = require('cluster'); 231 | } catch(e) { 232 | // cluster module is not present. 233 | cluster = null 234 | } 235 | if (cluster) { 236 | // Then provide some functionality for a cluster-aware version of Node.js 237 | } else { 238 | // and some alternative for a cluster-unaware version. 239 | } 240 | ``` 241 | 242 | To exercise the second branch of the `if` statement, you can make proxyquire pretend the package isn't present by 243 | setting the stub for it to `null`. This works even if a `cluster` module is actually present. 244 | 245 | ```javascript 246 | var foo = proxyquire('./foo', { cluster: null }); 247 | ``` 248 | 249 | ## Forcing proxyquire to reload modules 250 | 251 | In most situations it is fine to have proxyquire behave exactly like nodejs `require`, i.e. modules that are loaded once 252 | get pulled from the cache the next time. 253 | 254 | For some tests however you need to ensure that the module gets loaded fresh everytime, i.e. if that causes initializing 255 | some dependency or some module state. 256 | 257 | For this purpose proxyquire exposes the `noPreserveCache` function. 258 | 259 | ```js 260 | // ensure we don't get any module from the cache, but to load it fresh every time 261 | var proxyquire = require('proxyquire').noPreserveCache(); 262 | 263 | var foo1 = proxyquire('./foo', stubs); 264 | var foo2 = proxyquire('./foo', stubs); 265 | var foo3 = require('./foo'); 266 | 267 | // foo1, foo2 and foo3 are different instances of the same module 268 | assert.notStrictEqual(foo1, foo2); 269 | assert.notStrictEqual(foo1, foo3); 270 | ``` 271 | 272 | `proxyquire.preserveCache` allows you to restore the behavior to match nodejs's `require` again. 273 | 274 | ```js 275 | proxyquire.preserveCache(); 276 | 277 | var foo1 = proxyquire('./foo', stubs); 278 | var foo2 = proxyquire('./foo', stubs); 279 | var foo3 = require('./foo'); 280 | 281 | // foo1, foo2 and foo3 are the same instance 282 | assert.strictEqual(foo1, foo2); 283 | assert.strictEqual(foo1, foo3); 284 | ``` 285 | 286 | 287 | ## Globally override require 288 | 289 | Use the `@global` property to override every `require` of a module, even transitively. 290 | 291 | ### Caveat 292 | 293 | You should **think very hard about alternatives before using this feature**. Why, because it's intrusive and as you'll 294 | see if you read on it changes the default behavior of module initialization which means that code runs differently 295 | during testing than it does normally. 296 | 297 | Additionally it **makes it harder to reason about how your tests work**. 298 | 299 | > Yeah, we are mocking `fs` three levels down in `bar`, so that's why we have to set it up when testing `foo` 300 | 301 | **WAAAT???** 302 | 303 | If you write proper unit tests you should never have a need for this. So here are some techniques to consider: 304 | 305 | - test each module in isolation 306 | - make sure your modules are small enough and do only one thing 307 | - stub out dependencies directly instead of stubbing something inside your dependencies 308 | - if you are testing `bar` and `bar` calls `foo.read` and `foo.read` calls `fs.readFile` proceed as follows 309 | - **do not** stub out `fs.readFile` globally 310 | - instead stub out `foo` so you can control what `foo.read` returns without ever even hitting `fs` 311 | 312 | 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 313 | far as I'll go ;) 314 | 315 | Watch out for more warnings below. 316 | 317 | ### Globally override require during module initialization 318 | 319 | ```javascript 320 | // foo.js 321 | var bar = require('./bar'); 322 | 323 | module.exports = function() { 324 | bar(); 325 | } 326 | 327 | // bar.js 328 | var baz = require('./baz'); 329 | 330 | module.exports = function() { 331 | baz.method(); 332 | } 333 | 334 | // baz.js 335 | module.exports = { 336 | method: function() { 337 | console.info('hello'); 338 | } 339 | } 340 | 341 | // test.js 342 | var bazStub = { 343 | method: function() { 344 | console.info('goodbye'); 345 | } 346 | }; 347 | 348 | var stubs = { 349 | './baz': Object.assign(bazStub, {'@global': true}) 350 | }; 351 | 352 | var proxyquire = require('proxyquire'); 353 | 354 | var foo = proxyquire('./foo', stubs); 355 | foo(); // 'goodbye' is printed to stdout 356 | ``` 357 | 358 | Be aware that when using global overrides **any module initialization code will be re-executed for each require.** 359 | 360 | This is not normally the case since node.js caches the return value of `require`, however to make global overrides work , 361 | `proxyquire` bypasses the module cache. This may cause **unexpected behaviour if a module's initialization causes side effects**. 362 | 363 | As an example consider this module which opens a file during its initialization: 364 | 365 | ```javascript 366 | var fs = require('fs') 367 | , C = require('C'); 368 | 369 | // will get executed twice 370 | var file = fs.openSync('/tmp/foo.txt', 'w'); 371 | 372 | module.exports = function() { 373 | return new C(file); 374 | }; 375 | ``` 376 | 377 | The file at `/tmp/foo.txt` could be created and/or truncated more than once. 378 | 379 | ### Why is proxyquire messing with my `require` cache? 380 | 381 | Say you have a module, C, that you wish to stub. You require module A which contains `require('B')`. Module B in turn 382 | contains `require('C')`. If module B has already been required elsewhere then when module A receives the cached version 383 | of module B and proxyquire would have no opportunity to inject the stub for C. 384 | 385 | Therefore when using the `@global` flag, `proxyquire` will bypass the `require` cache. 386 | 387 | ### Globally override require during module runtime 388 | 389 | Say you have a module that looks like this: 390 | 391 | ```javascript 392 | module.exports = function() { 393 | var d = require('d'); 394 | d.method(); 395 | }; 396 | ``` 397 | The invocation of `require('d')` will happen at runtime and not when the containing module is requested via `require`. 398 | If you want to globally override `d` above, use the `@runtimeGlobal` property: 399 | 400 | ```javascript 401 | var stubs = { 402 | 'd': { 403 | method: function(val) { 404 | console.info('hello world'); 405 | }, 406 | '@runtimeGlobal': true 407 | } 408 | }; 409 | ``` 410 | 411 | This will cause module setup code to be re-executed just like `@global`, but with the difference that it will happen 412 | every time the module is requested via `require` at runtime as no module will ever be cached. 413 | 414 | This can cause subtle bugs so if you can guarantee that your modules will not vary their `require` behaviour at runtime, 415 | use `@global` instead. 416 | 417 | ## Configuring proxyquire by setting stub properties 418 | 419 | 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: 420 | 421 | ```js 422 | function foo () {} 423 | proxyquire('./bar', { 424 | foo: Object.assign(foo, {'@global': true}) 425 | }); 426 | ``` 427 | 428 | And if your stub is in a separate module where `module.exports = foo`: 429 | 430 | ```js 431 | var foostub = require('../stubs/foostub'); 432 | proxyquire('bar', { 433 | foo: Object.assign(foostub, {'@global': true}) 434 | }); 435 | ``` 436 | 437 | # Backwards Compatibility for proxyquire v0.3.x 438 | 439 | Compatibility mode with proxyquire v0.3.x **has been removed**. 440 | 441 | 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. 442 | 443 | # Examples 444 | 445 | **We are testing foo which depends on bar:** 446 | 447 | ```javascript 448 | // bar.js module 449 | module.exports = { 450 | toAtm: function (val) { return 0.986923267 * val; } 451 | }; 452 | 453 | // foo.js module 454 | // requires bar which we will stub out in tests 455 | var bar = require('./bar'); 456 | [ ... ] 457 | 458 | ``` 459 | 460 | **Tests:** 461 | 462 | ```javascript 463 | // foo-test.js module which is one folder below foo.js (e.g., in ./tests/) 464 | 465 | /* 466 | * Option a) Resolve and override in one step: 467 | */ 468 | var foo = proxyquire('../foo', { 469 | './bar': { toAtm: function (val) { return 0; /* wonder what happens now */ } } 470 | }); 471 | 472 | // [ .. run some tests .. ] 473 | 474 | /* 475 | * Option b) Resolve with empty stub and add overrides later 476 | */ 477 | var barStub = { }; 478 | 479 | var foo = proxyquire('../foo', { './bar': barStub }); 480 | 481 | // Add override 482 | barStub.toAtm = function (val) { return 0; /* wonder what happens now */ }; 483 | 484 | [ .. run some tests .. ] 485 | 486 | // Change override 487 | barStub.toAtm = function (val) { return -1 * val; /* or now */ }; 488 | 489 | [ .. run some tests .. ] 490 | 491 | // Resolve foo and override multiple of its dependencies in one step - oh my! 492 | var foo = proxyquire('./foo', { 493 | './bar' : { 494 | toAtm: function (val) { return 0; /* wonder what happens now */ } 495 | } 496 | , path : { 497 | extname: function (file) { return 'exterminate the name of ' + file; } 498 | } 499 | }); 500 | ``` 501 | 502 | # More Examples 503 | 504 | For more examples look inside the [examples folder](https://github.com/thlorenz/proxyquire/tree/master/examples/) or 505 | look through the [tests](https://github.com/thlorenz/proxyquire/blob/master/test/proxyquire.js) 506 | 507 | **Specific Examples:** 508 | 509 | - test async APIs synchronously: [examples/async](https://github.com/thlorenz/proxyquire/tree/master/examples/async). 510 | - using proxyquire with [Sinon.JS](http://sinonjs.org/): [examples/sinon](https://github.com/thlorenz/proxyquire/tree/master/examples/sinon). 511 | -------------------------------------------------------------------------------- /examples/api/api-test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('assert') 4 | var stats = require('./samples/stats') 5 | var proxyquire = require('../..') 6 | var file = '/some/path/test.ext' 7 | var foo 8 | var fooCut 9 | var fooWild 10 | var cutBarStub = { bar: function () { return 'barber' } } 11 | var wildBarStub = { bar: function () { return 'barbar' } } 12 | 13 | foo = proxyquire('./samples/foo', { }) 14 | fooCut = proxyquire('./samples/foo', { './bar': cutBarStub }) 15 | fooWild = proxyquire('./samples/foo', { './bar': wildBarStub }) 16 | 17 | assert.strictEqual(stats.fooRequires(), 3) 18 | 19 | assert.strictEqual(foo.bigBar(), 'BAR') 20 | assert.strictEqual(fooCut.bigBar(), 'BARBER') 21 | assert.strictEqual(fooWild.bigBar(), 'BARBAR') 22 | 23 | // non overridden keys call thru by default 24 | assert.strictEqual(foo.bigRab(), 'RAB') 25 | assert.strictEqual(fooCut.bigRab(), 'RAB') 26 | 27 | // non overridden module path untouched 28 | assert.strictEqual(foo.bigExt(file), '.EXT') 29 | assert.strictEqual(fooCut.bigExt(file), '.EXT') 30 | assert.strictEqual(fooWild.bigExt(file), '.EXT') 31 | assert.strictEqual(foo.bigBas(file), 'TEST.EXT') 32 | assert.strictEqual(fooCut.bigBas(file), 'TEST.EXT') 33 | assert.strictEqual(fooWild.bigBas(file), 'TEST.EXT') 34 | 35 | // overriding keys after require works for both inline and non inline requires 36 | cutBarStub.bar = function () { return 'friseur' } 37 | cutBarStub.rab = function () { return 'rabarber' } 38 | 39 | assert.strictEqual(fooCut.bigBar(), 'FRISEUR') 40 | assert.strictEqual(fooCut.bigRab(), 'RABARBER') 41 | 42 | // autofilling keys on delete only works for inline requires 43 | cutBarStub.bar = undefined 44 | assert.strictEqual(fooCut.bigBar(), 'BAR') 45 | 46 | cutBarStub.rab = undefined 47 | assert.throws(fooCut.bigRab) 48 | 49 | // turn off callThru feature via noCallThru 50 | // not turned off 51 | foo = proxyquire('./samples/foo', { 52 | path: { 53 | extname: function (file) { return 'Exterminate, exterminate the ' + file } 54 | } 55 | }) 56 | 57 | assert.strictEqual(foo.bigExt(file), 'EXTERMINATE, EXTERMINATE THE /SOME/PATH/TEST.EXT') 58 | assert.strictEqual(foo.bigBas(file), 'TEST.EXT') 59 | 60 | // turned off 61 | foo = proxyquire('./samples/foo', { 62 | path: { 63 | extname: function (file) { return 'Exterminate, exterminate the ' + file }, 64 | '@noCallThru': true 65 | } 66 | }) 67 | 68 | assert.strictEqual(foo.bigExt(file), 'EXTERMINATE, EXTERMINATE THE /SOME/PATH/TEST.EXT') 69 | assert.throws(foo.bigBas) 70 | 71 | // turned off globally 72 | // not turned back on per module 73 | 74 | foo = proxyquire 75 | .noCallThru() 76 | .load('./samples/foo', { 77 | path: { 78 | extname: function (file) { return 'Exterminate, exterminate the ' + file } 79 | } 80 | }) 81 | 82 | assert.throws(foo.bigBas) 83 | 84 | // turned back on per module 85 | 86 | foo = proxyquire 87 | .noCallThru() 88 | .load('./samples/foo', { 89 | path: { 90 | extname: function (file) { return 'Exterminate, exterminate the ' + file }, 91 | '@noCallThru': false 92 | } 93 | }) 94 | 95 | assert.strictEqual(foo.bigBas(file), 'TEST.EXT') 96 | 97 | // turned back on globally 98 | 99 | foo = proxyquire 100 | .callThru() 101 | .load('./samples/foo', { 102 | path: { 103 | extname: function (file) { return 'Exterminate, exterminate the ' + file } 104 | } 105 | }) 106 | 107 | assert.strictEqual(foo.bigBas(file), 'TEST.EXT') 108 | 109 | // turned back off per module 110 | 111 | foo = proxyquire 112 | .callThru() 113 | .load('./samples/foo', { 114 | path: { 115 | extname: function (file) { return 'Exterminate, exterminate the ' + file }, 116 | '@noCallThru': true 117 | } 118 | }) 119 | 120 | assert.throws(foo.bigBas) 121 | 122 | console.log('*** All Asserts passed ***') 123 | -------------------------------------------------------------------------------- /examples/api/samples/bar.js: -------------------------------------------------------------------------------- 1 | function bar () { 2 | return 'bar' 3 | } 4 | 5 | function rab () { 6 | return 'rab' 7 | } 8 | 9 | module.exports = { bar: bar, rab: rab } 10 | -------------------------------------------------------------------------------- /examples/api/samples/foo.js: -------------------------------------------------------------------------------- 1 | var stats = require('./stats') 2 | var bar = require('./bar') 3 | var path = require('path') 4 | 5 | stats.incFooRequires() 6 | 7 | function bigBar () { 8 | // inline require 9 | return require('./bar').bar().toUpperCase() 10 | } 11 | 12 | function bigRab () { 13 | // module wide require 14 | return bar.rab().toUpperCase() 15 | } 16 | 17 | function bigExt (file) { 18 | return path.extname(file).toUpperCase() 19 | } 20 | 21 | function bigBas (file) { 22 | return path.basename(file).toUpperCase() 23 | } 24 | 25 | module.exports = { 26 | bigBar: bigBar, 27 | bigRab: bigRab, 28 | bigExt: bigExt, 29 | bigBas: bigBas 30 | } 31 | -------------------------------------------------------------------------------- /examples/api/samples/stats.js: -------------------------------------------------------------------------------- 1 | var fooRequires = 0 2 | module.exports = { 3 | fooRequires: function () { return fooRequires }, 4 | incFooRequires: function () { fooRequires++ } 5 | } 6 | -------------------------------------------------------------------------------- /examples/async/foo-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var path = require('path') 4 | 5 | require('../example-utils').listModuleAndTests(path.resolve(__dirname, '/foo.js'), __filename) 6 | 7 | // Overriding callbacks that would normally be async will cause them to call back immediately 8 | // Thus allowing you to run synchronous tests against async APIs. 9 | 10 | var proxyquire = require('../..') 11 | var assert = require('assert') 12 | var readdirError = new Error('some error') 13 | var fsStub = { } 14 | var calledBack 15 | 16 | var foo = proxyquire('./foo', { fs: fsStub }) 17 | 18 | /* 19 | * Test caps locking of returned files 20 | */ 21 | fsStub.readdir = function (dir, cb) { cb(null, ['file1', 'file2']) } 22 | 23 | calledBack = false 24 | foo.filesAllCaps('./somedir', function (err, files) { 25 | assert.strictEqual(err, null) 26 | assert.strictEqual(files[0], 'FILE1') 27 | assert.strictEqual(files[1], 'FILE2') 28 | 29 | calledBack = true 30 | }) 31 | 32 | // fs.readdir and thus filesAllCaps calls back before we get here, which means the code ran synchronously 33 | assert(calledBack) 34 | 35 | /* 36 | * Test error propagation 37 | */ 38 | fsStub.readdir = function (dir, cb) { cb(readdirError) } 39 | 40 | foo.filesAllCaps('./somedir', function (err, files) { 41 | assert.strictEqual(err, readdirError) 42 | }) 43 | 44 | console.log('*** All asserts passed ***') 45 | -------------------------------------------------------------------------------- /examples/async/foo.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | 3 | module.exports.filesAllCaps = function (dir, cb) { 4 | fs.readdir(dir, function (err, files) { 5 | if (err) cb(err) 6 | else { 7 | cb( 8 | null 9 | , files.map(function (f) { return f.toUpperCase() }) 10 | ) 11 | } 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /examples/example-utils.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | 3 | module.exports.listModuleAndTests = function (module, tests) { 4 | console.log( 5 | '\n**********\n' + 6 | '* Module:*\n' + 7 | '**********\n\n' + 8 | fs.readFileSync(module).toString() 9 | ) 10 | 11 | console.log( 12 | '**********\n' + 13 | '* Tests: *\n' + 14 | '**********\n' + 15 | fs.readFileSync(tests).toString() 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /examples/simple/foo.inlineoverride.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var path = require('path') 4 | 5 | require('../example-utils').listModuleAndTests(path.resolve(__dirname, '/foo.js'), __filename) 6 | 7 | var proxyquire = require('../..') 8 | var assert = require('assert') 9 | var foo 10 | 11 | // no overrides yet, so path.extname behaves normally 12 | foo = proxyquire('./foo', {}) 13 | assert.strictEqual(foo.extnameAllCaps('file.txt'), '.TXT') 14 | 15 | // override path.extname 16 | foo = proxyquire('./foo', { 17 | path: { extname: function (file) { return 'Exterminate, exterminate the ' + file } } 18 | }) 19 | 20 | // path.extname now behaves as we told it to 21 | assert.strictEqual(foo.extnameAllCaps('file.txt'), 'EXTERMINATE, EXTERMINATE THE FILE.TXT') 22 | 23 | // path.basename on the other hand still functions as before 24 | assert.strictEqual(foo.basenameAllCaps('/a/b/file.txt'), 'FILE.TXT') 25 | 26 | console.log('*** All asserts passed ***') 27 | -------------------------------------------------------------------------------- /examples/simple/foo.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | 3 | module.exports.extnameAllCaps = function (file) { 4 | return path.extname(file).toUpperCase() 5 | } 6 | 7 | module.exports.basenameAllCaps = function (file) { 8 | return path.basename(file).toUpperCase() 9 | } 10 | -------------------------------------------------------------------------------- /examples/simple/foo.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var path = require('path') 4 | 5 | require('../example-utils').listModuleAndTests(path.resolve(__dirname, '/foo.js'), __filename) 6 | 7 | var proxyquire = require('../..') 8 | var assert = require('assert') 9 | var pathStub = { } 10 | 11 | // when not overridden, path.extname behaves normally 12 | var foo = proxyquire('./foo', { path: pathStub }) 13 | assert.strictEqual(foo.extnameAllCaps('file.txt'), '.TXT') 14 | 15 | // override path.extname 16 | pathStub.extname = function (file) { return 'Exterminate, exterminate the ' + file } 17 | 18 | // path.extname now behaves as we told it to 19 | assert.strictEqual(foo.extnameAllCaps('file.txt'), 'EXTERMINATE, EXTERMINATE THE FILE.TXT') 20 | 21 | // path.basename and all other path module methods still function as before 22 | assert.strictEqual(foo.basenameAllCaps('/a/b/file.txt'), 'FILE.TXT') 23 | 24 | console.log('*** All asserts passed ***') 25 | -------------------------------------------------------------------------------- /examples/sinon/foo-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* 4 | * These examples demonstrate how to use proxyquire with Sinon.JS (). 5 | * Run these tests with mocha (). 6 | * e.g., mocha foo-tests.js 7 | */ 8 | 9 | var proxyquire = require('../..') 10 | var sinon = require('sinon') 11 | var assert = require('assert') 12 | var fs = require('fs') 13 | var path = require('path') 14 | var foo 15 | 16 | // Stubbing return values 17 | describe('when path.extname(file) returns ".markdown"', function () { 18 | var extnameStub 19 | var file = 'somefile' 20 | 21 | before(function () { 22 | extnameStub = sinon.stub(path, 'extname') 23 | foo = proxyquire('./foo', { path: { extname: extnameStub } }) 24 | 25 | extnameStub.withArgs(file).returns('.markdown') 26 | }) 27 | 28 | after(function () { 29 | path.extname.restore() 30 | }) 31 | 32 | it('extnameAllCaps returns ".MARKDOWN"', function () { 33 | assert.strictEqual(foo.extnameAllCaps(file), '.MARKDOWN') 34 | }) 35 | }) 36 | 37 | // Stubbing callbacks 38 | describe('when fs.readdir calls back with ["file1", "file2"]', function () { 39 | var readdirStub 40 | 41 | before(function () { 42 | readdirStub = sinon.stub(fs, 'readdir') 43 | foo = proxyquire('./foo', { fs: { readdir: readdirStub } }) 44 | 45 | readdirStub.withArgs('../simple').yields(null, ['file1', 'file2']) 46 | }) 47 | 48 | after(function () { 49 | fs.readdir.restore() 50 | }) 51 | 52 | it('filesAllCaps calls back with ["FILE1", "FILE2"]', function (done) { 53 | foo.filesAllCaps('../simple', function (err, files) { 54 | assert.strictEqual(err, null) 55 | assert.strictEqual(files[0], 'FILE1') 56 | assert.strictEqual(files[1], 'FILE2') 57 | done() 58 | }) 59 | }) 60 | }) 61 | 62 | describe('when fs.readdir returns an error', function () { 63 | var readdirError, 64 | readdirStub 65 | 66 | before(function () { 67 | readdirStub = sinon.stub(fs, 'readdir') 68 | foo = proxyquire('./foo', { fs: { readdir: readdirStub } }) 69 | 70 | readdirError = new Error('some error') 71 | readdirStub.withArgs('../simple').yields(readdirError, null) 72 | }) 73 | 74 | after(function () { 75 | fs.readdir.restore() 76 | }) 77 | 78 | it('filesAllCaps calls back with that error', function (done) { 79 | foo.filesAllCaps('../simple', function (err, files) { 80 | assert.strictEqual(err, readdirError) 81 | assert.strictEqual(files, null) 82 | done() 83 | }) 84 | }) 85 | }) 86 | 87 | // Spying 88 | describe('when calling filesAllCaps with "../simple"', function () { 89 | var readdirSpy 90 | 91 | before(function () { 92 | readdirSpy = sinon.spy(fs, 'readdir') 93 | foo = proxyquire('./foo', { fs: { readdir: readdirSpy } }) 94 | }) 95 | 96 | after(function () { 97 | fs.readdir.restore() 98 | }) 99 | 100 | it('calls fs.readdir with "../simple"', function (done) { 101 | foo.filesAllCaps('../simple', function (err, files) { 102 | assert.ifError(err) 103 | assert(fs.readdir.calledOnce) 104 | assert.strictEqual(fs.readdir.getCall(0).args[0], '../simple') 105 | done() 106 | }) 107 | }) 108 | }) 109 | -------------------------------------------------------------------------------- /examples/sinon/foo.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var path = require('path') 3 | 4 | module.exports.filesAllCaps = function (dir, cb) { 5 | fs.readdir(dir, function (err, files) { 6 | if (err) cb(err) 7 | else { 8 | cb( 9 | null 10 | , files.map(function (f) { return f.toUpperCase() }) 11 | ) 12 | } 13 | }) 14 | } 15 | 16 | module.exports.extnameAllCaps = function (file) { 17 | return path.extname(file).toUpperCase() 18 | } 19 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var Proxyquire = require('./lib/proxyquire') 4 | 5 | // delete this module from the cache to force re-require in order to allow resolving test module via parent.module 6 | delete require.cache[require.resolve(__filename)] 7 | 8 | module.exports = new Proxyquire(module.parent) 9 | module.exports.compat = function () { 10 | 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') 11 | } 12 | -------------------------------------------------------------------------------- /lib/is.js: -------------------------------------------------------------------------------- 1 | var is = {}; 2 | 3 | ['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'].forEach(function (name) { 4 | is[name] = function (obj) { 5 | return Object.prototype.toString.call(obj) === '[object ' + name + ']' 6 | } 7 | }) 8 | 9 | is.Object = function (obj) { 10 | /* eslint no-new-object: "off" */ 11 | return obj === new Object(obj) 12 | } 13 | 14 | module.exports = is 15 | -------------------------------------------------------------------------------- /lib/proxyquire-error.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var util = require('util') 4 | 5 | function ProxyquireError (msg) { 6 | this.name = 'ProxyquireError' 7 | Error.captureStackTrace(this, ProxyquireError) 8 | this.message = msg || 'An error occurred inside proxyquire.' 9 | } 10 | 11 | util.inherits(ProxyquireError, Error) 12 | 13 | module.exports = ProxyquireError 14 | -------------------------------------------------------------------------------- /lib/proxyquire.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | /* jshint laxbreak:true, loopfunc:true */ 3 | 4 | var Module = require('module') 5 | var path = require('path') 6 | var resolve = require('resolve') 7 | var ProxyquireError = require('./proxyquire-error') 8 | var is = require('./is') 9 | var assert = require('assert') 10 | var fillMissingKeys = require('fill-keys') 11 | var moduleNotFoundError = require('module-not-found-error') 12 | var hasOwnProperty = Object.prototype.hasOwnProperty 13 | 14 | function validateArguments (request, stubs) { 15 | var msg = (function getMessage () { 16 | if (!request) { return 'Missing argument: "request". Need it to resolve desired module.' } 17 | 18 | if (!stubs) { return 'Missing argument: "stubs". If no stubbing is needed, use regular require instead.' } 19 | 20 | if (!is.String(request)) { return 'Invalid argument: "request". Needs to be a requirable string that is the module to load.' } 21 | 22 | if (!is.Object(stubs)) { return 'Invalid argument: "stubs". Needs to be an object containing overrides e.g., {"path": { extname: function () { ... } } }.' } 23 | })() 24 | 25 | if (msg) throw new ProxyquireError(msg) 26 | } 27 | 28 | function Proxyquire (parent) { 29 | var self = this 30 | var fn = self.load.bind(self) 31 | var proto = Proxyquire.prototype 32 | 33 | this._parent = parent 34 | this._preserveCache = true 35 | 36 | Object.keys(proto) 37 | .forEach(function (key) { 38 | if (is.Function(proto[key])) fn[key] = self[key].bind(self) 39 | }) 40 | 41 | self.fn = fn 42 | return fn 43 | } 44 | 45 | /** 46 | * Disables call thru, which determines if keys of original modules will be used 47 | * when they weren't stubbed out. 48 | * @name noCallThru 49 | * @function 50 | * @private 51 | * @return {object} The proxyquire function to allow chaining 52 | */ 53 | Proxyquire.prototype.noCallThru = function () { 54 | this._noCallThru = true 55 | return this.fn 56 | } 57 | 58 | /** 59 | * Enables call thru, which determines if keys of original modules will be used 60 | * when they weren't stubbed out. 61 | * @name callThru 62 | * @function 63 | * @private 64 | * @return {object} The proxyquire function to allow chaining 65 | */ 66 | Proxyquire.prototype.callThru = function () { 67 | this._noCallThru = false 68 | return this.fn 69 | } 70 | 71 | /** 72 | * Will make proxyquire remove the requested modules from the `require.cache` in order to force 73 | * them to be reloaded the next time they are proxyquired. 74 | * This behavior differs from the way nodejs `require` works, but for some tests this maybe useful. 75 | * 76 | * @name noPreserveCache 77 | * @function 78 | * @private 79 | * @return {object} The proxyquire function to allow chaining 80 | */ 81 | Proxyquire.prototype.noPreserveCache = function () { 82 | this._preserveCache = false 83 | return this.fn 84 | } 85 | 86 | /** 87 | * Restores proxyquire caching behavior to match the one of nodejs `require` 88 | * 89 | * @name preserveCache 90 | * @function 91 | * @private 92 | * @return {object} The proxyquire function to allow chaining 93 | */ 94 | Proxyquire.prototype.preserveCache = function () { 95 | this._preserveCache = true 96 | return this.fn 97 | } 98 | 99 | /** 100 | * Loads a module using the given stubs instead of their normally resolved required modules. 101 | * @param request The requirable module path to load. 102 | * @param stubs The stubs to use. e.g., { "path": { extname: function () { ... } } } 103 | * @return {*} A newly resolved module with the given stubs. 104 | */ 105 | Proxyquire.prototype.load = function (request, stubs) { 106 | validateArguments(request, stubs) 107 | 108 | // Find out if any of the passed stubs are global overrides 109 | for (var key in stubs) { 110 | var stub = stubs[key] 111 | 112 | if (stub === null) continue 113 | 114 | if (typeof stub === 'undefined') { 115 | throw new ProxyquireError('Invalid stub: "' + key + '" cannot be undefined') 116 | } 117 | 118 | if (hasOwnProperty.call(stub, '@global')) { 119 | this._containsGlobal = true 120 | } 121 | 122 | if (hasOwnProperty.call(stub, '@runtimeGlobal')) { 123 | this._containsGlobal = true 124 | this._containsRuntimeGlobal = true 125 | } 126 | } 127 | 128 | // Ignore the module cache when return the requested module 129 | return this._withoutCache(this._parent, stubs, request, this._parent.require.bind(this._parent, request)) 130 | } 131 | 132 | // Resolves a stub relative to a module. 133 | // `baseModule` is the module we're resolving from. `pathToResolve` is the 134 | // module we want to resolve (i.e. the string passed to `require()`). 135 | Proxyquire.prototype._resolveModule = function (baseModule, pathToResolve, stubs) { 136 | try { 137 | return resolve.sync(pathToResolve, { 138 | basedir: path.dirname(baseModule), 139 | extensions: Object.keys(require.extensions), 140 | paths: Module.globalPaths 141 | }) 142 | } catch (err) { 143 | // If this is not a relative path (e.g. "foo" as opposed to "./foo"), and 144 | // we couldn't resolve it, then we just let the path through unchanged. 145 | // It's safe to do this, because if two different modules require "foo", 146 | // they both expect to get back the same thing. 147 | if (pathToResolve[0] !== '.') { 148 | return pathToResolve 149 | } 150 | 151 | // If `pathToResolve` is relative, then it is *not* safe to return it, 152 | // since a file in one directory that requires "./foo" expects to get 153 | // back a different module than one that requires "./foo" from another 154 | // directory. However, if !this._preserveCache, then we don't want to 155 | // throw, since we can resolve modules that don't exist. Resolve as 156 | // best we can. We also need to check if the relative module has @noCallThru. 157 | var resolvedPath = path.resolve(path.dirname(baseModule), pathToResolve) 158 | var moduleNoCallThru 159 | if (hasOwnProperty.call(stubs, pathToResolve) && stubs[pathToResolve]) { 160 | // pathToResolve is currently relative on stubs from _withoutCache() call 161 | moduleNoCallThru = hasOwnProperty.call(stubs[pathToResolve], '@noCallThru') ? stubs[pathToResolve]['@noCallThru'] : undefined 162 | } else if (hasOwnProperty.call(stubs, resolvedPath) && stubs[resolvedPath]) { 163 | // after _withoutCache() alters stubs paths to be absolute 164 | moduleNoCallThru = hasOwnProperty.call(stubs[resolvedPath], '@noCallThru') ? stubs[resolvedPath]['@noCallThru'] : undefined 165 | } 166 | if (!this._preserveCache || this._noCallThru || moduleNoCallThru) { 167 | return resolvedPath 168 | } 169 | 170 | throw err 171 | } 172 | } 173 | 174 | // This replaces a module's require function 175 | Proxyquire.prototype._require = function (module, stubs, path) { 176 | assert(typeof path === 'string', 'path must be a string') 177 | assert(path, 'missing path') 178 | 179 | var resolvedPath = this._resolveModule(module.filename, path, stubs) 180 | if (hasOwnProperty.call(stubs, resolvedPath)) { 181 | var stub = stubs[resolvedPath] 182 | if (stub === null) { 183 | // Mimic the module-not-found exception thrown by node.js. 184 | throw moduleNotFoundError(path) 185 | } 186 | 187 | if (hasOwnProperty.call(stub, '@noCallThru') ? !stub['@noCallThru'] : !this._noCallThru) { 188 | fillMissingKeys(stub, Module._load(path, module)) 189 | } 190 | 191 | // We are top level or this stub is marked as global 192 | if (module.parent === this._parent || hasOwnProperty.call(stub, '@global') || hasOwnProperty.call(stub, '@runtimeGlobal')) { 193 | return stub 194 | } 195 | } 196 | 197 | // Only ignore the cache if we have global stubs 198 | if (this._containsRuntimeGlobal) { 199 | return this._withoutCache(module, stubs, path, Module._load.bind(Module, path, module)) 200 | } else { 201 | return Module._load(path, module) 202 | } 203 | } 204 | 205 | Proxyquire.prototype._withoutCache = function (module, stubs, path, func) { 206 | // Temporarily disable the cache - either per-module or globally if we have global stubs 207 | var restoreCache = this._disableCache(module, path) 208 | var resolvedPath = Module._resolveFilename(path, module) 209 | 210 | // Resolve all stubs to absolute paths. 211 | stubs = Object.keys(stubs) 212 | .reduce(function (result, stubPath) { 213 | var resolvedStubPath = this._resolveModule(resolvedPath, stubPath, stubs) 214 | result[resolvedStubPath] = stubs[stubPath] 215 | return result 216 | }.bind(this), {}) 217 | // Override all require extension handlers 218 | var restoreExtensionHandlers = this._overrideExtensionHandlers(module, stubs) 219 | 220 | try { 221 | // Execute the function that needs the module cache disabled 222 | return func() 223 | } finally { 224 | // Restore the cache if we are preserving it 225 | if (this._preserveCache) { 226 | restoreCache() 227 | } else { 228 | var ids = [resolvedPath].concat(Object.keys(stubs).filter(Boolean)) 229 | ids.forEach(function (id) { 230 | delete require.cache[id] 231 | }) 232 | } 233 | 234 | // Finally restore the original extension handlers 235 | restoreExtensionHandlers() 236 | } 237 | } 238 | 239 | Proxyquire.prototype._disableCache = function (module, path) { 240 | if (this._containsGlobal) { 241 | // empty the require cache because if we are stubbing C but requiring A, 242 | // and if A requires B and B requires C, then B and C might be cached already 243 | // and we'll never get the chance to return our stub 244 | return this._disableGlobalCache() 245 | } 246 | 247 | // Temporarily delete the SUT from the require cache 248 | return this._disableModuleCache(path, module) 249 | } 250 | 251 | Proxyquire.prototype._disableGlobalCache = function () { 252 | var cache = require.cache 253 | require.cache = Module._cache = {} 254 | 255 | for (var id in cache) { 256 | // Keep native modules (i.e. `.node` files). 257 | // Otherwise, Node.js would throw a “Module did not self-register” 258 | // error upon requiring it a second time. 259 | // See https://github.com/nodejs/node/issues/5016. 260 | if (/\.node$/.test(id)) { 261 | require.cache[id] = cache[id] 262 | } 263 | } 264 | 265 | // Return a function that will undo what we just did 266 | return function () { 267 | // Keep native modules which were added to the cache in the meantime. 268 | for (var id in require.cache) { 269 | if (/\.node$/.test(id)) { 270 | cache[id] = require.cache[id] 271 | } 272 | } 273 | 274 | require.cache = Module._cache = cache 275 | } 276 | } 277 | 278 | Proxyquire.prototype._disableModuleCache = function (path, module) { 279 | // Find the ID (location) of the SUT, relative to the parent 280 | var id = Module._resolveFilename(path, module) 281 | 282 | var cached = Module._cache[id] 283 | delete Module._cache[id] 284 | 285 | // Return a function that will undo what we just did 286 | return function () { 287 | if (cached) { 288 | Module._cache[id] = cached 289 | } else { 290 | delete Module._cache[id] 291 | } 292 | } 293 | } 294 | 295 | Proxyquire.prototype._overrideExtensionHandlers = function (module, resolvedStubs) { 296 | /* eslint node/no-deprecated-api: [error, {ignoreGlobalItems: ["require.extensions"]}] */ 297 | var originalExtensions = {} 298 | var self = this 299 | 300 | Object.keys(require.extensions).forEach(function (extension) { 301 | // Store the original so we can restore it later 302 | if (!originalExtensions[extension]) { 303 | originalExtensions[extension] = require.extensions[extension] 304 | } 305 | 306 | // Override the default handler for the requested file extension 307 | require.extensions[extension] = function (module, filename) { 308 | // Override the require method for this module 309 | module.require = self._require.bind(self, module, resolvedStubs) 310 | 311 | return originalExtensions[extension](module, filename) 312 | } 313 | }) 314 | 315 | // Return a function that will undo what we just did 316 | return function () { 317 | Object.keys(originalExtensions).forEach(function (extension) { 318 | require.extensions[extension] = originalExtensions[extension] 319 | }) 320 | } 321 | } 322 | 323 | module.exports = Proxyquire 324 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "proxyquire", 3 | "version": "2.1.3", 4 | "description": "Proxies nodejs require in order to allow overriding dependencies during testing.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "standard && mocha" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/thlorenz/proxyquire.git" 12 | }, 13 | "keywords": [ 14 | "require", 15 | "dependency", 16 | "injection", 17 | "di", 18 | "inject", 19 | "swap", 20 | "test", 21 | "mock", 22 | "stub" 23 | ], 24 | "author": "Thorsten Lorenz", 25 | "license": "MIT", 26 | "devDependencies": { 27 | "mocha": "^5.2.0", 28 | "native-hello-world": "^1.0.0", 29 | "should": "^13.2.3", 30 | "sinon": "^7.3.2", 31 | "standard": "^13.0.1" 32 | }, 33 | "dependencies": { 34 | "fill-keys": "^1.0.2", 35 | "module-not-found-error": "^1.0.1", 36 | "resolve": "^1.11.1" 37 | }, 38 | "standard": { 39 | "env": [ 40 | "mocha" 41 | ] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tea.yaml: -------------------------------------------------------------------------------- 1 | # https://tea.xyz/what-is-this-file 2 | --- 3 | version: 1.0.0 4 | codeOwners: 5 | - '0x9ce16bA88E268D1cF6a63830EB331D1b9622fb7d' 6 | quorum: 1 7 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --require should 2 | --reporter spec 3 | --ui bdd 4 | --growl 5 | test -------------------------------------------------------------------------------- /test/proxyquire-api.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('assert') 4 | var realFoo = require('./samples/foo') 5 | 6 | var stubs = { 7 | path: { 8 | extname: function () {}, 9 | basename: function () {} 10 | } 11 | } 12 | 13 | describe('api', function () { 14 | describe('default export', function () { 15 | var proxyquire = require('..') 16 | 17 | it('proxyquire can load', function () { 18 | var proxiedFoo = proxyquire.load('./samples/foo', stubs) 19 | 20 | assert.strictEqual(typeof proxiedFoo, 'object') 21 | assert.notStrictEqual(realFoo, proxiedFoo) 22 | }) 23 | 24 | it('proxyquire can callThru and then load', function () { 25 | var proxiedFoo = proxyquire.callThru().load('./samples/foo', stubs) 26 | 27 | assert.strictEqual(typeof proxiedFoo, 'object') 28 | assert.notStrictEqual(realFoo, proxiedFoo) 29 | }) 30 | 31 | it('proxyquire can noCallThru and then load', function () { 32 | var proxiedFoo = proxyquire.noCallThru().load('./samples/foo', stubs) 33 | 34 | assert.strictEqual(typeof proxiedFoo, 'object') 35 | assert.notStrictEqual(realFoo, proxiedFoo) 36 | }) 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /test/proxyquire-argumentvalidation.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('assert') 4 | var proxyquire = require('..') 5 | 6 | describe('Illegal parameters to resolve give meaningful errors', function () { 7 | var bar = { bar: function () { return 'bar' } } 8 | 9 | function throws (action, regex) { 10 | assert.throws(action, function (err) { 11 | return err.name === 'ProxyquireError' && regex.test(err.message) && regex.test(err.toString()) 12 | }) 13 | } 14 | 15 | describe('when I pass no request', function () { 16 | function act () { 17 | proxyquire(null, {}) 18 | } 19 | 20 | it('throws an exception explaining that a request path must be provided', function () { 21 | throws(act, /missing argument: "request"/i) 22 | }) 23 | }) 24 | 25 | describe('when I pass an object as a request', function () { 26 | function act () { 27 | proxyquire({ }, { './bar': bar }) 28 | } 29 | 30 | it('throws an exception explaining that request needs to be a requirable string', function () { 31 | throws(act, /invalid argument: "request".+needs to be a requirable string/i) 32 | }) 33 | }) 34 | 35 | describe('when I pass no stubs', function () { 36 | function act () { 37 | proxyquire('./samples/foo') 38 | } 39 | 40 | it('throws an exception explaining that resolve without stubs makes no sense', function () { 41 | throws(act, /missing argument: "stubs".+use regular require instead/i) 42 | }) 43 | }) 44 | 45 | describe('when I pass a string as stubs', function () { 46 | function act () { 47 | proxyquire('./samples/foo', 'stubs') 48 | } 49 | 50 | it('throws an exception explaining that stubs need to be an object', function () { 51 | throws(act, /invalid argument: "stubs".+needs to be an object/i) 52 | }) 53 | }) 54 | 55 | describe('when I pass an undefined stub', function () { 56 | function act () { 57 | proxyquire('./samples/foo', { 58 | myStub: undefined 59 | }) 60 | } 61 | 62 | it('throws an exception with the stub key', function () { 63 | throws(act, /Invalid stub: "myStub" cannot be undefined/) 64 | }) 65 | }) 66 | }) 67 | -------------------------------------------------------------------------------- /test/proxyquire-cache.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('assert') 4 | 5 | describe('Proxyquire', function () { 6 | describe('load()', function () { 7 | it('defaults to preserving the cache', function () { 8 | var original = require('./samples/foo') 9 | original.state = 'cached' 10 | 11 | var proxyquire = require('..') 12 | proxyquire('./samples/foo', { path: { } }) 13 | 14 | var foo = require('./samples/foo') 15 | assert.strictEqual('cached', foo.state) 16 | assert.strictEqual(foo, original) 17 | }) 18 | 19 | it('does not pollute the cache when module is proxyquired before it is loaded', function () { 20 | var proxyquire = require('..') 21 | 22 | proxyquire('./samples/no-call-thru-test', { './required': false }) 23 | var original = require('./samples/no-call-thru-test') 24 | 25 | assert.strictEqual(original.original, true) 26 | }) 27 | }) 28 | 29 | describe('preserveCache()', function () { 30 | it('returns a reference to itself, so it can be chained', function () { 31 | var proxyquire = require('..') 32 | assert.strictEqual(proxyquire.preserveCache(), proxyquire) 33 | }) 34 | 35 | it('has Proxyquire restore the cache for the module', function () { 36 | var original = require('./samples/foo') 37 | original.state = 'cached' 38 | 39 | var proxyquire = require('..') 40 | proxyquire.preserveCache() 41 | proxyquire.load('./samples/foo', { path: { } }) 42 | 43 | var foo = require('./samples/foo') 44 | assert.strictEqual('cached', foo.state) 45 | assert.strictEqual(foo, original) 46 | }) 47 | 48 | it('allows Singletons to function properly', function () { 49 | var original = require('./samples/foo-singleton').getInstance() 50 | 51 | var proxyquire = require('..') 52 | proxyquire.preserveCache() 53 | proxyquire.load('./samples/foo-singleton', { path: { } }).getInstance() 54 | 55 | var fooSingleton = require('./samples/foo-singleton').getInstance() 56 | assert.strictEqual(fooSingleton, original) 57 | }) 58 | }) 59 | 60 | describe('noPreserveCache()', function () { 61 | it('returns a reference to itself, so it can be chained', function () { 62 | var proxyquire = require('..') 63 | assert.strictEqual(proxyquire.noPreserveCache(), proxyquire) 64 | }) 65 | 66 | it('forces subsequent requires to reload the proxied module', function () { 67 | var original = require('./samples/foo') 68 | original.state = 'cached' 69 | 70 | var proxyquire = require('..') 71 | proxyquire.load('./samples/foo', { path: { } }) 72 | 73 | var cacheFoo = require('./samples/foo') 74 | assert.strictEqual('cached', cacheFoo.state) 75 | assert.strictEqual(cacheFoo, original) 76 | 77 | proxyquire.noPreserveCache() 78 | proxyquire.load('./samples/foo', { path: { } }) 79 | var foo = require('./samples/foo') 80 | assert.strictEqual('', foo.state) 81 | assert.notStrictEqual(foo, original) 82 | }) 83 | 84 | it('deletes the require.cache for the module being stubbed', function () { 85 | var proxyquire = require('..').noPreserveCache() 86 | 87 | proxyquire.load('./samples/foo', { path: { } }) 88 | assert.strictEqual(undefined, require.cache[require.resolve('./samples/foo')]) 89 | }) 90 | 91 | it('deletes the require.cache for the stubs', function () { 92 | var proxyquire = require('..').noPreserveCache() 93 | 94 | var bar = {} 95 | var foo = proxyquire.load('./samples/cache/foo', { './bar': bar }) 96 | bar.f.g = function () { return 'a' } 97 | bar.h = function () { return 'a' } 98 | 99 | assert.strictEqual(foo.bar.f.g(), 'a') 100 | assert.strictEqual(foo.bar.h(), 'a') 101 | 102 | foo = proxyquire.load('./samples/cache/foo', { './bar': {} }) 103 | assert.strictEqual(foo.bar.h(), 'h') 104 | assert.strictEqual(foo.bar.f.g(), 'g') 105 | 106 | assert.strictEqual(undefined, require.cache[require.resolve('./samples/cache/foo')]) 107 | assert.strictEqual(undefined, require.cache[require.resolve('./samples/cache/bar')]) 108 | }) 109 | 110 | it('silences errors when stub lookups fail', function () { 111 | var proxyquire = require('..').noPreserveCache() 112 | 113 | assert.doesNotThrow(function () { 114 | proxyquire.load('./samples/cache/foo', { './does-not-exist': {} }) 115 | }) 116 | }) 117 | }) 118 | }) 119 | -------------------------------------------------------------------------------- /test/proxyquire-compat.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | /* jshint asi:true */ 3 | 4 | var proxyquire = require('..') 5 | 6 | describe('when I try to use compat mode', function () { 7 | it('should let me know that I need to fix my code or downgrade', function () { 8 | proxyquire.compat.should.throw(/compat mode has been removed/) 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /test/proxyquire-extensions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var proxyquire = require('..').noCallThru() 4 | 5 | describe('when I require stubs with different extensions', function () { 6 | var res 7 | before(function () { 8 | res = proxyquire('./samples/extensions', { 9 | fs: 'fs.export', 10 | fn: function () { return 'fn.result' }, 11 | '/fs.json': 'fs.json.export', 12 | '/fn.node': 'fn.node.export' 13 | }) 14 | }) 15 | 16 | it('intercepts [] object', function () { 17 | res.fs.should.equal('fs.export') 18 | }) 19 | 20 | it('intercepts [] function', function () { 21 | res.fn().should.equal('fn.result') 22 | }) 23 | 24 | it('intercepts [.json] object', function () { 25 | res['/fs.json'].should.equal('fs.json.export') 26 | }) 27 | 28 | it('intercepts [.node] object', function () { 29 | res['/fn.node'].should.equal('fn.node.export') 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /test/proxyquire-global.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('assert') 4 | var realFoo = require('./samples/global/foo') 5 | 6 | var proxyquire = require('..') 7 | 8 | describe('global flags set', function () { 9 | it('should override require globally', function () { 10 | var stubs = { 11 | './baz': { 12 | method: function () { 13 | return true 14 | }, 15 | '@global': true 16 | } 17 | } 18 | 19 | var proxiedFoo = proxyquire('./samples/global/foo', stubs) 20 | 21 | assert.strictEqual(realFoo(), false) 22 | assert.strictEqual(proxiedFoo(), true) 23 | }) 24 | 25 | it('should override require globally even when require\'s execution is deferred', function () { 26 | var stubs = { 27 | './baz': { 28 | method: function () { 29 | return true 30 | }, 31 | '@runtimeGlobal': true 32 | } 33 | } 34 | 35 | var proxiedFoo = proxyquire('./samples/global/foo-deferred', stubs) 36 | 37 | assert.strictEqual(realFoo(), false) 38 | assert.strictEqual(proxiedFoo(), true) 39 | }) 40 | 41 | it('should not throw when a native module is required a second time', function () { 42 | var stubs = { 43 | foo: { 44 | '@global': true 45 | } 46 | } 47 | 48 | proxyquire('native-hello-world', stubs) 49 | proxyquire('native-hello-world', stubs) 50 | }) 51 | }) 52 | 53 | describe('global flags not set', function () { 54 | it('should not override require globally', function () { 55 | var stubs = { 56 | './baz': { 57 | method: function () { 58 | return true 59 | } 60 | } 61 | } 62 | 63 | var proxiedFoo = proxyquire('./samples/global/foo', stubs) 64 | 65 | assert.strictEqual(realFoo(), false) 66 | assert.strictEqual(proxiedFoo(), false) 67 | }) 68 | 69 | it('should not override require globally even when require\'s execution is deferred', function () { 70 | var stubs = { 71 | './baz': { 72 | method: function () { 73 | return true 74 | } 75 | } 76 | } 77 | 78 | var proxiedFoo = proxyquire('./samples/global/foo-deferred', stubs) 79 | 80 | assert.strictEqual(realFoo(), false) 81 | assert.strictEqual(proxiedFoo(), false) 82 | }) 83 | }) 84 | -------------------------------------------------------------------------------- /test/proxyquire-independence.js: -------------------------------------------------------------------------------- 1 | /* jshint asi:true */ 2 | 'use strict' 3 | 4 | var assert = require('assert') 5 | var proxyquire = require('..') 6 | 7 | describe('Multiple requires of same module don\'t affect each other', function () { 8 | describe('Given I require foo stubbed with bar1 as foo1 and foo stubbed with bar2 as foo2', function () { 9 | var foo1 10 | var foo2 11 | var bar1 = { bar: function () { return 'bar1' } } 12 | var bar2 = { bar: function () { return 'bar2' } } 13 | 14 | before(function () { 15 | foo1 = proxyquire('./samples/foo', { './bar': bar1 }) 16 | foo2 = proxyquire('./samples/foo', { './bar': bar2 }) 17 | }) 18 | 19 | it('foo1.bigBar() == "BAR1"', function () { 20 | assert.strictEqual(foo1.bigBar(), 'BAR1') 21 | }) 22 | 23 | it('foo2.bigBar() == "BAR2"', function () { 24 | assert.strictEqual(foo2.bigBar(), 'BAR2') 25 | }) 26 | 27 | describe('and I change bar1.bar() to return barone', function () { 28 | before(function () { 29 | bar1.bar = function () { return 'barone' } 30 | }) 31 | 32 | it('foo1.bigBar() == "BARONE"', function () { 33 | assert.strictEqual(foo1.bigBar(), 'BARONE') 34 | }) 35 | 36 | it('foo2.bigBar() == "BAR2"', function () { 37 | assert.strictEqual(foo2.bigBar(), 'BAR2') 38 | }) 39 | }) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /test/proxyquire-non-object.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('assert') 4 | var proxyquire = require('..') 5 | var stats = require('./samples/stats') 6 | 7 | describe('Given foo requires the boof, foonum and foobool modules and boof is a string, foonum is a Number and foobool is a bool', function () { 8 | var foo 9 | var boofber = 'a_string' 10 | var foonumber = 4 11 | var fooboolber = false 12 | var fooarray = ['x', 'y', 'z'] 13 | 14 | describe('When I resolve foo with boofber stub as boof.', function () { 15 | before(function () { 16 | stats.reset() 17 | foo = proxyquire('./samples/foo', { './boof': boofber }) 18 | }) 19 | 20 | it('foo is required 1 times', function () { 21 | assert.strictEqual(stats.fooRequires(), 1) 22 | }) 23 | 24 | describe('foo\'s boof is boofber', function () { 25 | it('foo.boof == boofber', function () { 26 | assert.strictEqual(foo.boof, boofber) 27 | }) 28 | }) 29 | }) 30 | 31 | describe('When I resolve foo with foonumber stub as foonum.', function () { 32 | before(function () { 33 | stats.reset() 34 | foo = proxyquire('./samples/foo', { './foonum': foonumber }) 35 | }) 36 | 37 | it('foo is required 1 times', function () { 38 | assert.strictEqual(stats.fooRequires(), 1) 39 | }) 40 | 41 | describe('foo\'s foonum is foonumber', function () { 42 | it('foo.foonum == foonumber', function () { 43 | assert.strictEqual(foo.foonum, foonumber) 44 | }) 45 | }) 46 | }) 47 | 48 | describe('When I resolve foo with fooboolber stub as foobool.', function () { 49 | before(function () { 50 | stats.reset() 51 | foo = proxyquire('./samples/foo', { './foobool': fooboolber }) 52 | }) 53 | 54 | it('foo is required 1 times', function () { 55 | assert.strictEqual(stats.fooRequires(), 1) 56 | }) 57 | 58 | describe('foo\'s foobool is fooboolber', function () { 59 | it('foo.foobool == fooboolber', function () { 60 | assert.strictEqual(foo.foobool, fooboolber) 61 | }) 62 | }) 63 | }) 64 | 65 | describe('When I resolve foo with ./fooarray stub as fooarray.', function () { 66 | before(function () { 67 | stats.reset() 68 | foo = proxyquire('./samples/foo', { './fooarray': fooarray }) 69 | }) 70 | 71 | it('foo is required 1 times', function () { 72 | assert.strictEqual(stats.fooRequires(), 1) 73 | }) 74 | 75 | describe('foo\'s fooarray is fooarray', function () { 76 | it('foo.fooarray is fooarray', function () { 77 | assert.deepStrictEqual(foo.fooarray, ['x', 'y', 'z']) 78 | }) 79 | }) 80 | }) 81 | 82 | describe('When I resolve foo with ./fooarray stub as empty.', function () { 83 | before(function () { 84 | stats.reset() 85 | foo = proxyquire('./samples/foo', { './fooarray': [] }) 86 | }) 87 | 88 | it('foo is required 1 times', function () { 89 | assert.strictEqual(stats.fooRequires(), 1) 90 | }) 91 | 92 | describe('foo\'s fooarray is the original', function () { 93 | it('foo.fooarray is empty', function () { 94 | assert.deepStrictEqual(foo.fooarray, ['a', 'b', 'c']) 95 | }) 96 | }) 97 | }) 98 | 99 | describe('When I resolve foo with ./fooarray stub as empty with @noCallThru.', function () { 100 | before(function () { 101 | stats.reset() 102 | var empty = [] 103 | Object.defineProperty(empty, '@noCallThru', { value: true }) 104 | foo = proxyquire('./samples/foo', { './fooarray': empty }) 105 | }) 106 | 107 | it('foo is required 1 times', function () { 108 | assert.strictEqual(stats.fooRequires(), 1) 109 | }) 110 | 111 | describe('foo\'s fooarray is empty', function () { 112 | it('foo.fooarray is empty', function () { 113 | assert.deepStrictEqual(foo.fooarray, []) 114 | }) 115 | }) 116 | }) 117 | }) 118 | -------------------------------------------------------------------------------- /test/proxyquire-notexisting.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('assert') 4 | var proxyquire = require('..') 5 | var path = require('path') 6 | var fooPath = path.join(__dirname, './samples/notexisting/foo.js') 7 | var fooRelativePath = path.join(__dirname, './samples/notexisting/foo-relative.js') 8 | 9 | describe('When resolving foo that requires stubbed /not/existing/bar.json', function () { 10 | it('throws an error', function () { 11 | assert.throws(function () { 12 | proxyquire(fooPath, { 13 | '/not/existing/bar.json': { config: 'bar\'s config' } 14 | }) 15 | }) 16 | }) 17 | }) 18 | 19 | describe('When resolving foo that requires stubbed /not/existing/bar.json with @noCallThru', function () { 20 | it('resolves foo with stubbed bar', function () { 21 | var foo = proxyquire(fooPath, { 22 | '/not/existing/bar.json': { config: 'bar\'s config', '@noCallThru': true } 23 | }) 24 | assert.strictEqual(foo.config, 'bar\'s config') 25 | }) 26 | }) 27 | 28 | describe('When resolving foo that requires stubbed /not/existing/bar.json with noCallThru()', function () { 29 | it('resolves foo with stubbed bar', function () { 30 | proxyquire.noCallThru() 31 | var foo = proxyquire(fooPath, { 32 | '/not/existing/bar.json': { config: 'bar\'s config' } 33 | }) 34 | assert.strictEqual(foo.config, 'bar\'s config') 35 | proxyquire.callThru() 36 | }) 37 | }) 38 | 39 | describe('When resolving foo-relative that requires relative stubbed ../not/existing/bar.json with @noCallThru', function () { 40 | it('resolves foo-relative with stubbed bar', function () { 41 | var fooRelative = proxyquire(fooRelativePath, { 42 | '../not/existing/bar.json': { config: 'bar\'s config', '@noCallThru': true } 43 | }) 44 | assert.strictEqual(fooRelative.config, 'bar\'s config') 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /test/proxyquire-relative-paths.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* jshint asi:true */ 4 | 5 | var proxyquire = require('..') 6 | 7 | describe('When requiring relative paths, they should be relative to the proxyrequired module', function () { 8 | it('should return the correct result', function () { 9 | var result = proxyquire('./samples/relative-paths/a/index.js', { './util': { c: 'c' } }) 10 | result.should.eql({ a: 'a', c: 'c' }) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /test/proxyquire-remove.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | /* jshint asi:true */ 3 | 4 | var assert = require('assert') 5 | var proxyquire = require('..') 6 | var path = require('path') 7 | var fooPath = path.join(__dirname, './samples/foo.js') 8 | 9 | describe('When resolving foo that requires nulled file package', function () { 10 | it('throws an error', function () { 11 | assert.throws(function () { 12 | proxyquire(fooPath, { path: null }) 13 | }, function (error) { 14 | return error.code === 'MODULE_NOT_FOUND' 15 | }) 16 | }) 17 | }) 18 | 19 | describe('When resolving foo that optionally requires nulled crypto package', function () { 20 | it('catches when resolving crypto', function () { 21 | var foo = proxyquire(fooPath, { crypto: null }) 22 | assert.strictEqual(foo.bigCrypto(), 'caught') 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /test/proxyquire-sub-dependencies.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | /* jshint asi:true */ 3 | 4 | var assert = require('assert') 5 | var proxyquire = require('..') 6 | 7 | describe('When resolving foo that requires bar and stubbed baz where bar requires unstubbed baz', function () { 8 | var bazStub, 9 | foo, 10 | baz 11 | 12 | before(function () { 13 | bazStub = { 14 | testexport: 'stubbed' 15 | } 16 | 17 | foo = proxyquire('./samples/sub-dependencies/foo', { 18 | './baz': bazStub 19 | }) 20 | 21 | baz = require('./samples/sub-dependencies/baz') 22 | }) 23 | 24 | it('does not stub baz in bar', function () { 25 | assert.strictEqual(foo.bar.baz.testexport, 'test') 26 | }) 27 | 28 | it('does not affect a normal baz import', function () { 29 | assert.strictEqual(baz.testexport, 'test') 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /test/proxyquire.js: -------------------------------------------------------------------------------- 1 | /* jshint asi:true */ 2 | 'use strict' 3 | 4 | var assert = require('assert') 5 | var proxyquire = require('..') 6 | var stats = require('./samples/stats') 7 | 8 | describe('Given foo requires the bar and path modules and bar.bar() returns "bar"', function () { 9 | var file = '/folder/test.ext' 10 | var foo 11 | var foober 12 | var barber = { bar: function () { return 'barber' } } 13 | 14 | describe('When I resolve foo with no overrides to bar as foo and resolve foo with barber stub as foober.', function () { 15 | before(function () { 16 | stats.reset() 17 | foo = proxyquire('./samples/foo', { './bar': { /* no overrides */ } }) 18 | foober = proxyquire('./samples/foo', { './bar': barber }) 19 | }) 20 | 21 | it('foo is required 2 times', function () { 22 | assert.strictEqual(stats.fooRequires(), 2) 23 | }) 24 | 25 | describe('foo\'s bar is unchanged', function () { 26 | it('foo.bigBar() == "BAR"', function () { 27 | assert.strictEqual(foo.bigBar(), 'BAR') 28 | }) 29 | }) 30 | 31 | describe('only stubbed modules have overrides in foober', function () { 32 | it('foober.bigBar() == "BARBER"', function () { 33 | assert.strictEqual(foober.bigBar(), 'BARBER') 34 | }) 35 | 36 | it('foober.bigExt("/folder/test.ext") == ".EXT"', function () { 37 | assert.strictEqual(foober.bigExt(file), '.EXT') 38 | }) 39 | 40 | it('foober.bigBas("/folder/test.ext") == "TEST.EXT"', function () { 41 | assert.strictEqual(foober.bigBas(file), 'TEST.EXT') 42 | }) 43 | }) 44 | 45 | describe('when I override keys of stubs after resolve', function () { 46 | before(function () { 47 | barber.bar = function () { return 'friseur' } 48 | barber.rab = function () { return 'rabarber' } 49 | }) 50 | 51 | it('overrides behavior when module is required inside function call', function () { 52 | assert.strictEqual(foober.bigBar(), 'FRISEUR') 53 | }) 54 | 55 | it('overrides behavior when module is required on top of file', function () { 56 | assert.strictEqual(foober.bigRab(), 'RABARBER') 57 | }) 58 | 59 | describe('and then delete overrides of stubs after resolve', function () { 60 | beforeEach(function () { 61 | delete barber.bar 62 | delete barber.rab 63 | }) 64 | 65 | it('reverts to original behavior when module is required inside function call', function () { 66 | assert.strictEqual(foober.bigBar(), 'BAR') 67 | }) 68 | 69 | it('doesn\'t properly revert to original behavior when module is required on top of file ', function () { 70 | assert.throws(foober.bigRab) 71 | }) 72 | }) 73 | }) 74 | }) 75 | 76 | describe('When foo.bigExt() returns capitalized path.extname and foo.bigBas() returns capitalized path.basename', function () { 77 | describe('and path.extname(file) is stubbed to return "override " + file', function () { 78 | describe('and callThru was not changed globally or for path module', function () { 79 | before(function () { 80 | foo = proxyquire('./samples/foo', { 81 | path: { 82 | extname: function (file) { return 'override ' + file } 83 | } 84 | }) 85 | }) 86 | 87 | it('foo.bigExt(file) == "OVERRIDE /FOLDER/TEST.EXT"', function () { 88 | assert.strictEqual(foo.bigExt(file), 'OVERRIDE /FOLDER/TEST.EXT') 89 | }) 90 | 91 | it('foo.bigBas(file) == "TEST.EXT"', function () { 92 | assert.strictEqual(foo.bigBas(file), 'TEST.EXT') 93 | }) 94 | }) 95 | 96 | describe('and callThru is turned off for path module', function () { 97 | before(function () { 98 | foo = proxyquire('./samples/foo', { 99 | path: { 100 | extname: function (file) { return 'override ' + file }, '@noCallThru': true 101 | } 102 | }) 103 | }) 104 | 105 | it('foo.bigExt(file) == "OVERRIDE /FOLDER/TEST.EXT"', function () { 106 | assert.strictEqual(foo.bigExt(file), 'OVERRIDE /FOLDER/TEST.EXT') 107 | }) 108 | 109 | it('foo.bigBas(file) throws', function () { 110 | assert.throws(foo.bigBas) 111 | }) 112 | }) 113 | 114 | describe('and callThru was turned off globally', function () { 115 | var $proxyquire 116 | before(function () { 117 | $proxyquire = proxyquire.noCallThru() 118 | }) 119 | 120 | describe('and not changed for path module', function () { 121 | before(function () { 122 | foo = $proxyquire('./samples/foo', { 123 | path: { 124 | extname: function (file) { return 'override ' + file } 125 | } 126 | }) 127 | }) 128 | 129 | it('foo.bigExt(file) == "OVERRIDE /FOLDER/TEST.EXT"', function () { 130 | assert.strictEqual(foo.bigExt(file), 'OVERRIDE /FOLDER/TEST.EXT') 131 | }) 132 | 133 | it('foo.bigBas(file) throws', function () { 134 | assert.throws(foo.bigBas) 135 | }) 136 | }) 137 | 138 | describe('and turned back on for path module', function () { 139 | before(function () { 140 | foo = $proxyquire('./samples/foo', { 141 | path: { 142 | extname: function (file) { return 'override ' + file }, '@noCallThru': false 143 | } 144 | }) 145 | }) 146 | 147 | it('foo.bigExt(file) == "OVERRIDE /FOLDER/TEST.EXT"', function () { 148 | assert.strictEqual(foo.bigExt(file), 'OVERRIDE /FOLDER/TEST.EXT') 149 | }) 150 | 151 | it('foo.bigBas(file) == "TEST.EXT"', function () { 152 | assert.strictEqual(foo.bigBas(file), 'TEST.EXT') 153 | }) 154 | }) 155 | 156 | describe('and turned back on globally', function () { 157 | before(function () { 158 | foo = $proxyquire 159 | .callThru() 160 | .load('./samples/foo', { 161 | path: { 162 | extname: function (file) { return 'override ' + file } 163 | } 164 | }) 165 | }) 166 | 167 | it('foo.bigExt(file) == "OVERRIDE /FOLDER/TEST.EXT"', function () { 168 | assert.strictEqual(foo.bigExt(file), 'OVERRIDE /FOLDER/TEST.EXT') 169 | }) 170 | 171 | it('foo.bigBas(file) == "TEST.EXT"', function () { 172 | assert.strictEqual(foo.bigBas(file), 'TEST.EXT') 173 | }) 174 | }) 175 | }) 176 | }) 177 | }) 178 | }) 179 | -------------------------------------------------------------------------------- /test/samples/bar.js: -------------------------------------------------------------------------------- 1 | function bar () { 2 | return 'bar' 3 | } 4 | 5 | function rab () { 6 | return 'rab' 7 | } 8 | 9 | module.exports = { bar: bar, rab: rab } 10 | -------------------------------------------------------------------------------- /test/samples/boof.js: -------------------------------------------------------------------------------- 1 | module.exports = 'boof' 2 | -------------------------------------------------------------------------------- /test/samples/cache/bar.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | f: { 3 | g: function () { 4 | return 'g' 5 | } 6 | }, 7 | h: function () { 8 | return 'h' 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/samples/cache/foo.js: -------------------------------------------------------------------------------- 1 | var bar = require('./bar') 2 | 3 | module.exports = { bar: bar } 4 | -------------------------------------------------------------------------------- /test/samples/extensions.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | fs: require('fs'), 3 | fn: require('fn'), 4 | '/fs.json': require('/fs.json'), 5 | '/fn.node': require('/fn.node') 6 | } 7 | -------------------------------------------------------------------------------- /test/samples/foo-singleton.js: -------------------------------------------------------------------------------- 1 | function FooSingleton () { } 2 | 3 | var instance = null 4 | exports.getInstance = function () { 5 | if (instance === null) { 6 | instance = new FooSingleton() 7 | } 8 | 9 | return instance 10 | } 11 | -------------------------------------------------------------------------------- /test/samples/foo.js: -------------------------------------------------------------------------------- 1 | var stats = require('./stats') 2 | var bar = require('./bar') 3 | var boof = require('./boof') 4 | var foonum = require('./foonum') 5 | var foobool = require('./foobool') 6 | var fooarray = require('./fooarray') 7 | var path = require('path') 8 | var crypto 9 | 10 | // Require if present. 11 | try { crypto = require('crypto') } catch (e) { crypto = 'caught' } 12 | 13 | stats.incFooRequires() 14 | 15 | function bigBar () { 16 | // inline require 17 | return require('./bar').bar().toUpperCase() 18 | } 19 | 20 | function bigRab () { 21 | // module wide require 22 | return bar.rab().toUpperCase() 23 | } 24 | 25 | function bigExt (file) { 26 | return path.extname(file).toUpperCase() 27 | } 28 | 29 | function bigBas (file) { 30 | return path.basename(file).toUpperCase() 31 | } 32 | 33 | function bigCrypto () { 34 | return crypto 35 | } 36 | 37 | module.exports = { 38 | bigBar: bigBar, 39 | bigRab: bigRab, 40 | bigExt: bigExt, 41 | bigBas: bigBas, 42 | boof: boof, 43 | foonum: foonum, 44 | foobool: foobool, 45 | fooarray: fooarray, 46 | bigCrypto: bigCrypto, 47 | state: '' 48 | } 49 | -------------------------------------------------------------------------------- /test/samples/fooarray.js: -------------------------------------------------------------------------------- 1 | module.exports = ['a', 'b', 'c'] 2 | -------------------------------------------------------------------------------- /test/samples/foobool.js: -------------------------------------------------------------------------------- 1 | module.exports = true 2 | -------------------------------------------------------------------------------- /test/samples/foonum.js: -------------------------------------------------------------------------------- 1 | module.exports = 3 2 | -------------------------------------------------------------------------------- /test/samples/global/bar.js: -------------------------------------------------------------------------------- 1 | var baz = require('./baz') 2 | 3 | module.exports = function () { 4 | return baz.method() 5 | } 6 | -------------------------------------------------------------------------------- /test/samples/global/baz.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | method: function () { 3 | return false 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/samples/global/foo-deferred.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function () { 3 | var bar = require('./bar') 4 | 5 | return bar() 6 | } 7 | -------------------------------------------------------------------------------- /test/samples/global/foo.js: -------------------------------------------------------------------------------- 1 | var bar = require('./bar') 2 | 3 | module.exports = function () { 4 | return bar() 5 | } 6 | -------------------------------------------------------------------------------- /test/samples/no-call-thru-test/index.js: -------------------------------------------------------------------------------- 1 | exports.original = require('./required') 2 | -------------------------------------------------------------------------------- /test/samples/no-call-thru-test/required.js: -------------------------------------------------------------------------------- 1 | module.exports = true 2 | -------------------------------------------------------------------------------- /test/samples/notexisting/foo-relative.js: -------------------------------------------------------------------------------- 1 | var bar = require('../not/existing/bar.json') 2 | 3 | module.exports = { 4 | config: bar.config 5 | } 6 | -------------------------------------------------------------------------------- /test/samples/notexisting/foo.js: -------------------------------------------------------------------------------- 1 | var bar = require('/not/existing/bar.json') 2 | 3 | module.exports = { 4 | config: bar.config 5 | } 6 | -------------------------------------------------------------------------------- /test/samples/relative-paths/a/index.js: -------------------------------------------------------------------------------- 1 | var util = require('./util') 2 | require('../b') 3 | 4 | module.exports = util 5 | -------------------------------------------------------------------------------- /test/samples/relative-paths/a/util.js: -------------------------------------------------------------------------------- 1 | exports.a = 'a' 2 | -------------------------------------------------------------------------------- /test/samples/relative-paths/b/index.js: -------------------------------------------------------------------------------- 1 | require('./util') 2 | -------------------------------------------------------------------------------- /test/samples/relative-paths/b/util.js: -------------------------------------------------------------------------------- 1 | exports.b = 'b' 2 | -------------------------------------------------------------------------------- /test/samples/stats.js: -------------------------------------------------------------------------------- 1 | var fooRequires = 0 2 | module.exports = { 3 | fooRequires: function () { return fooRequires }, 4 | incFooRequires: function () { fooRequires++ }, 5 | reset: function () { fooRequires = 0 } 6 | } 7 | -------------------------------------------------------------------------------- /test/samples/sub-dependencies/bar.js: -------------------------------------------------------------------------------- 1 | var baz = require('./baz') 2 | 3 | module.exports = { 4 | baz: baz 5 | } 6 | -------------------------------------------------------------------------------- /test/samples/sub-dependencies/baz.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testexport: 'test' 3 | } 4 | -------------------------------------------------------------------------------- /test/samples/sub-dependencies/foo.js: -------------------------------------------------------------------------------- 1 | var baz = require('./baz') 2 | var bar = require('./bar') 3 | 4 | module.exports = { 5 | baz: baz, 6 | bar: bar 7 | } 8 | --------------------------------------------------------------------------------