├── .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 [](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 |
--------------------------------------------------------------------------------