This repository has been archived by the owner on Apr 30, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 60
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit d230816
Showing
4 changed files
with
416 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,24 @@ | |||
Copyrights for code authored by Yahoo! Inc. is licensed under the following | |||
terms: | |||
|
|||
MIT License | |||
|
|||
Copyright (c) 2011 Yahoo! Inc. All Rights Reserved. | |||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to | |||
deal in the Software without restriction, including without limitation the | |||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |||
sell copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
|
|||
The above copyright notice and this permission notice shall be included in | |||
all copies or substantial portions of the Software. | |||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,145 @@ | |||
# Mockery - Simplifying the use of mocks with Node.js | |||
|
|||
If you've tried working with mocks in Node.js, you've no doubt discovered that | |||
it's not so easy to get your mocks hooked up in the face of Node's module | |||
loading system. When your source-under-test pulls in its dependencies through | |||
`require`, you want your mocks provided, instead of the original module, | |||
to enable true unit testing of your code. | |||
|
|||
This is exactly the problem Mockery is designed to solve. Mockery gives you a | |||
simple and easy to use API with which you can hook in your mocks without having | |||
to get your hands dirty with the `require` cache or other Node implementation | |||
details. | |||
|
|||
Mockery is *not* a mocking framework. It lets you work more easily with your | |||
framework of choice (or no framework) to get your mocks hooked in to all the | |||
right places in the code you need to test. | |||
|
|||
## Installation | |||
|
|||
Just use npm: | |||
|
|||
npm install mockery | |||
|
|||
## Enabling mockery | |||
|
|||
When enabled, Mockery intercepts *all* `require` calls, regardless of where | |||
those calls are being made from. Thus it's almost always desirable to bracket | |||
your usage as narrowly as possible. | |||
|
|||
If you're using a typical unit testing framework, you might enable and disable | |||
Mockery in the test setup and teardown functions for your test cases. Something | |||
like this: | |||
|
|||
setUp: function() { | |||
mockery.enable(); | |||
}, | |||
tearDown: function() { | |||
mockery.disable(); | |||
} | |||
|
|||
## Registering mocks | |||
|
|||
You register your mocks with Mockery to tell it which mocks to provide for which | |||
`require` calls. For example: | |||
|
|||
var fsMock = { | |||
stat: function (path, cb) { /* your mock code */ } | |||
}; | |||
mockery.registerMock('fs', fsMock); | |||
|
|||
The arguments to `registerMock` are as follows: | |||
|
|||
* _module_, the name or path of the module for which a mock is being | |||
registered. This must exactly match the argument to `require`; there is no | |||
"clever" matching. | |||
* _mock_, the mock to be provided. Whatever is provided here is what will | |||
become the result of subsequent `require` calls; that is, the `exports` of the | |||
module. | |||
|
|||
If you no longer want your mock to be used, you can deregister it: | |||
|
|||
mockery.deregisterMock('fs'); | |||
|
|||
Now the original module will be provided for any subsequent `require` calls. | |||
|
|||
## Registering substitutes | |||
|
|||
Sometimes you want to implement your mock itself as a module, especially if it's | |||
more complicated and you'll be reusing it more widely. In that case, you can | |||
tell Mockery to substitute that module for the original one. For example: | |||
|
|||
mockery.registerSubstitute('fs', 'fs-mock'); | |||
|
|||
Now any `require` invocation for 'fs' will be satisfied by loading the 'fs-mock' | |||
module instead. | |||
|
|||
The arguments to `registerSubstitute` are as follows: | |||
|
|||
* _module_, the name or path of the module for which a substitute is being | |||
registered. This must exactly match the argument to `require`; there is no | |||
"clever" matching. | |||
* _substitute_, the name or path of the module to substitute for _module_. | |||
|
|||
If you no longer want your substitute to be used, you can deregister it: | |||
|
|||
mockery.deregisterSubstitute('fs'); | |||
|
|||
Now the original module will be provided for any subsequent `require` calls. | |||
|
|||
## Registering allowable modules | |||
|
|||
If you enable Mockery and _don't_ mock or substitute a module that is later | |||
loaded via `require`, Mockery will print a warning to the console to tell you | |||
that. This is so that you don't inadvertently use downstream modules without | |||
being aware of them. By registering a module as "allowable", you tell Mockery | |||
that you know about its use, and then Mockery won't print the warning. | |||
|
|||
The most common use case for this is your source-under-test, which obviously | |||
you'll want to load without warnings. For example: | |||
|
|||
mockery.registerAllowable('./my-source-under-test'); | |||
|
|||
As with `registerMock` and `registerSubstitute`, the first argument, _module_, | |||
is the name or path of the module as it would be provided to `require`. Once | |||
again, you can deregister it if you need to: | |||
|
|||
mockery.deregisterAllowable('./my-source-under-test'); | |||
|
|||
### Unhooking | |||
|
|||
By default, the Node module loader will load a given module only once, caching | |||
the loaded module for the lifetime of the process. When you're using Mockery, | |||
this is almost always what you want. _Almost_. In relatively rare situations, | |||
you may find that you need to use different mocks for different test cases | |||
for the same source-under-test. (This is not the same as supplying different | |||
test data in the same mock; here we're talking about providing different | |||
functions for a module's `exports`.) | |||
|
|||
To do this, your source-under-test must be unhooked from Node's module loading | |||
system, such that it can be loaded again with new mocks. You do this by passing | |||
a second argument, _unhook_, to `registerAllowable`, like this: | |||
|
|||
mockery.registerAllowable('./my-source-under-test', true); | |||
|
|||
When you subsequently deregister your source-under-test, Mockery will unhook it | |||
from the Node module loading system as well as deregistering it. | |||
|
|||
## Deregistering everything | |||
|
|||
Since it's such a common use case, especially when you're using a unit test | |||
framework and its setup and teardown functions, Mockery provides a convenience | |||
function to deregister everything: | |||
|
|||
mockery.deregisterAll(); | |||
|
|||
This will deregister all mocks, substitutes, and allowable modules, as well as | |||
unhooking any hooked modules. | |||
|
|||
## The name | |||
|
|||
Mockery is to mocks as rookery is to rooks. | |||
|
|||
## License | |||
|
|||
Mockery is licensed under the [MIT License](http://github.com/mfncooper/mockery/raw/master/LICENSE). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,222 @@ | |||
/* | |||
Copyrights for code authored by Yahoo! Inc. is licensed under the following | |||
terms: | |||
MIT License | |||
Copyright (c) 2011 Yahoo! Inc. All Rights Reserved. | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to | |||
deal in the Software without restriction, including without limitation the | |||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |||
sell copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in | |||
all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||
DEALINGS IN THE SOFTWARE. | |||
*/ | |||
|
|||
/* | |||
* A library that enables the hooking of the standard 'require' function, such | |||
* that a (possibly partial) mock implementation can be provided instead. This | |||
* is most useful for running unit tests, since any dependency obtained through | |||
* 'require' can be mocked out. | |||
*/ | |||
|
|||
var m = require('module'), | |||
registeredMocks = {}, | |||
registeredSubstitutes = {}, | |||
registeredAllowables = {}, | |||
originalLoader = null; | |||
|
|||
/* | |||
* The (private) loader replacement that is used when hooking is enabled. It | |||
* does the work of returning a mock or substitute when configured, reporting | |||
* non-allowed modules, and invoking the original loader when appropriate. | |||
* The signature of this function *must* match that of Node's Module._load, | |||
* since it will replace that when mockery is enabled. | |||
*/ | |||
function hookedLoader(request, parent, isMain) { | |||
var subst, allow, file; | |||
|
|||
if (!originalLoader) { | |||
throw new Error("Loader has not been hooked"); | |||
} | |||
|
|||
if (registeredMocks.hasOwnProperty(request)) { | |||
return registeredMocks[request]; | |||
} else if (registeredSubstitutes.hasOwnProperty(request)) { | |||
subst = registeredSubstitutes[request]; | |||
if (!subst.module && subst.name) { | |||
subst.module = originalLoader(subst.name, parent, isMain); | |||
} | |||
if (!subst.module) { | |||
throw new Error("Misconfigured substitute for '" + request + "'"); | |||
} | |||
return subst.module; | |||
} else { | |||
if (!registeredAllowables.hasOwnProperty(request)) { | |||
console.warn("WARNING: loading non-allowed module: " + request); | |||
} else { | |||
allow = registeredAllowables[request]; | |||
if (allow.unhook) { | |||
file = m._resolveFilename(request, parent)[1]; | |||
if (file.indexOf('/') !== -1 && allow.paths.indexOf(file) === -1) { | |||
allow.paths.push(file); | |||
} | |||
} | |||
} | |||
return originalLoader(request, parent, isMain); | |||
} | |||
} | |||
|
|||
/* | |||
* Enables mockery by hooking subsequent 'require' invocations. Note that *all* | |||
* 'require' invocations will be hooked until 'disable' is called. Calling this | |||
* function more than once will have no ill effects. | |||
*/ | |||
function enable() { | |||
if (originalLoader) { | |||
// Already hooked | |||
return; | |||
} | |||
/*jslint nomen: true */ | |||
originalLoader = m._load; | |||
m._load = hookedLoader; | |||
} | |||
|
|||
/* | |||
* Disables mockery by unhooking from the Node loader. No subsequent 'require' | |||
* invocations will be seen by mockery. Calling this function more than once | |||
* will have no ill effects. | |||
*/ | |||
function disable() { | |||
if (!originalLoader) { | |||
// Not hooked | |||
return; | |||
} | |||
/*jslint nomen: true */ | |||
m._load = originalLoader; | |||
originalLoader = null; | |||
} | |||
|
|||
/* | |||
* Register a mock object for the specified module. While mockery is enabled, | |||
* any subsequent 'require' for this module will return the mock object. The | |||
* mock need not mock out all original exports, but no fallback is provided | |||
* for anything not mocked and subsequently invoked. | |||
*/ | |||
function registerMock(mod, mock) { | |||
if (registeredMocks.hasOwnProperty(mod)) { | |||
console.warn("WARNING: Replacing existing mock for module: " + mod); | |||
} | |||
registeredMocks[mod] = mock; | |||
} | |||
|
|||
/* | |||
* Deregister a mock object for the specified module. A subsequent 'require' for | |||
* that module will revert to the previous behaviour (which, by default, means | |||
* falling back to the original 'require' behaviour). | |||
*/ | |||
function deregisterMock(mod) { | |||
if (registeredMocks.hasOwnProperty(mod)) { | |||
delete registeredMocks[mod]; | |||
} | |||
} | |||
|
|||
/* | |||
* Register a substitute module for the specified module. While mockery is | |||
* enabled, any subsequent 'require' for this module will be effectively | |||
* replaced by a 'require' for the substitute module. This is useful when | |||
* a mock implementation is itself implemented as a module. | |||
*/ | |||
function registerSubstitute(mod, subst) { | |||
if (registeredSubstitutes.hasOwnProperty(mod)) { | |||
console.warn("WARNING: Replacing existing substiute for module: " + mod); | |||
} | |||
registeredSubstitutes[mod] = subst; | |||
} | |||
|
|||
/* | |||
* Deregister a substitute module for the specified module. A subsequent | |||
* 'require' for that module will revert to the previous behaviour (which, by | |||
* default, means falling back to the original 'require' behaviour). | |||
*/ | |||
function deregisterSubstitute(mod) { | |||
if (registeredSubstitutes.hasOwnProperty(mod)) { | |||
delete registeredSubstitutes[mod]; | |||
} | |||
} | |||
|
|||
/* | |||
* Register a module as 'allowed', meaning that, even if a mock or substitute | |||
* for it has not been registered, mockery will not complain when it is loaded | |||
* via 'require'. This encourages the user to consciously declare the modules | |||
* that will be loaded and used in the original form, thus avoiding warnings. | |||
* | |||
* If 'unhook' is true, the module will be removed from the module cache when | |||
* it is deregistered. | |||
*/ | |||
function registerAllowable(mod, unhook) { | |||
registeredAllowables[mod] = { | |||
unhook: !!unhook, | |||
paths: [] | |||
}; | |||
} | |||
|
|||
/* | |||
* Deregister a module as 'allowed'. A subsequent 'require' for that module | |||
* will generate a warning that the module is not allowed, unless or until a | |||
* mock or substitute is registered for that module. | |||
*/ | |||
function deregisterAllowable(mod) { | |||
if (registeredAllowables.hasOwnProperty(mod)) { | |||
var allow = registeredAllowables[mod]; | |||
if (allow.unhook) { | |||
allow.paths.forEach(function (p) { | |||
delete m._cache[p]; | |||
}); | |||
} | |||
delete registeredAllowables[mod]; | |||
} | |||
} | |||
|
|||
/* | |||
* Deregister all mocks, substitutes, and allowed modules, resetting the state | |||
* to a clean slate. This does not affect the enabled / disabled state of | |||
* mockery, though. | |||
*/ | |||
function deregisterAll() { | |||
Object.keys(registeredAllowables).forEach(function (mod) { | |||
var allow = registeredAllowables[mod]; | |||
if (allow.unhook) { | |||
allow.paths.forEach(function (p) { | |||
delete m._cache[p]; | |||
}); | |||
} | |||
}); | |||
|
|||
registeredMocks = {}; | |||
registeredSubstitutes = {}; | |||
registeredAllowables = {}; | |||
} | |||
|
|||
// Exported functions | |||
exports.enable = enable; | |||
exports.disable = disable; | |||
exports.registerMock = registerMock; | |||
exports.registerSubstitute = registerSubstitute; | |||
exports.registerAllowable = registerAllowable; | |||
exports.deregisterMock = deregisterMock; | |||
exports.deregisterSubstitute = deregisterSubstitute; | |||
exports.deregisterAllowable = deregisterAllowable; | |||
exports.deregisterAll = deregisterAll; |
Oops, something went wrong.