Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
babel-plugin-mockable-imports
Advanced tools
A Babel plugin that modifies modules to enable mocking of their dependencies. The plugin was written with the following goals:
require
) module importsInstall the plugin:
npm install babel-plugin-mockable-imports
Then configure Babel to use it.
Note that you only want to apply this plugin for local development builds
and ideally only when running tests. One way to do this that works in multiple
environments is to use the env
option in your .babelrc
file.
For example, the following config in .babelrc will always load the plugin:
{
"plugins": ["mockable-imports"]
}
To load it only if the NODE_ENV
environment variable is set to development
use:
{
"env": {
"development": {
"plugins": ["mockable-imports"]
}
}
}
By default the plugin will try to avoid processing test modules. See the section on limiting mocking to specific files for details.
Each module in your codebase that this plugin is applied to will now export an
$imports
object with $mock
and $restore
methods. In your tests, import
the $imports
object from the module under test, call $mock
to replace
imports with stubs and $restore
to cleanup after the test.
For example, given this password.js
file that we want to test:
import {randomBytes} from 'crypto-functions';
export function generatePassword() {
return randomBytes(10)
.map(byte => byte.toString(16).padStart(2, '0'))
.join('');
}
We can write a test as follows, mocking the randomBytes
import:
import {generatePassword, $imports} from './password';
describe('generatePassword', () => {
afterEach(() => {
// Undo any mocks after each test run. This can be called even if no
// mocking was done by a test.
$imports.$restore();
});
it('generates expected password string', () => {
const fakeRandomBytes = length => Array(length).fill(42);
// Install mocks. The argument is a map of module paths (as used in the
// module being tested) to replacement exports.
$imports.$mock({
'crypto-functions': {
// Keys here are the names of the exports. Values are mocks.
randomBytes: fakeRandomBytes,
},
});
assert.equal(generatePassword(), '2a2a2a2a2a2a2a2a2a2a');
});
});
If the module you want to test uses CommonJS / Node style imports instead
(var someModule = require("some-module")
, see the section on
CommonJS.
If a module being mocked has a default export (eg. export default MyReactComponent
),
it can be mocked by setting the default
key.
For example, given ./Header.js
:
export default function Header() {
...
}
The Header
function can be mocked in tests for a different module using:
$imports.$mock({
'./Header': {default: FakeHeader},
'./Footer': {default: FakeFooter},
});
As a convenience, if the value for any of the keys in the object passed to
$mock
is a function, it is assumed to be a default export for the module.
This means that assuming FakeHeader
and FakeFooter
are functions,
the following is equivalent to the above:
$imports.$mock({
'./Header': FakeHeader,
'./Footer': FakeFooter,
});
Babel allows the set of plugins applied to files to be configured on a per directory basis. See the Babel configuration docs. You can also define overrides for more fine-grained rules.
As a convenience, the plugin by default skips any files in directories named
test
or __tests__
or their subdirectories. This can be configured using the
excludeDirs
option.
The plugin supports the following options:
excludeDirs
An array of directory names (eg. "tests") whose modules are excluded from this transformation by default.
excludeImportsFromModules
An array of module names which should be ignored when processing imports.
Any imports from these modules will not be mockable.
Default: ["proxyquire"]
.
The plugin has basic support for CommonJS. It will recognize the following patterns as imports:
var foo = require('./foo');
var { foo } = require('./foo');
var { foo: bar } = require('./foo');
Where var
may also be const
or let
. If the require
is wrapped or
contained within an expression it will not be processed.
When processing a CommonJS module the plugin still emits ES6 import
and
export
declarations, so transforming of ES6 import
/export
statements
to CommonJS must be enabled in Babel.
When the plugin processes a module, it gathers the set of imported symbols and
uses them to initialize an $imports
object, which is also exported from the
module. This object has a property corresponding to each import, in addition
to the $mock
and $restore
methods to temporarily modify those properties.
All references to imports are replaced with lookups of the corresponding
property of the $imports
object. For example, this code:
const someValue = dependencyA() + dependencyB();
Becomes:
const someValue = $imports.dependencyA() + $imports.dependencyB();
When you call $imports.$mock
in a test, the values of these properties are
temporarily changed to refer to the mocks instead. $imports.$restore
resets
the properties to their original values.
$imports
to every module it processes.
This may cause conflicts if you try to combine exports from multiple modules
using export * from <module>
. See
issue.
It can also cause problems if you have code which tries to loop over the
exports of a module and does not gracefully handle unexpected exports.import()
to obtain a promise for a module, or calling require
anywhere other than
at the top level of a module.If you encounter any problems using this plugin, please file an issue.
This plugin was created to work around subtle problems and inefficiencies that arose when using proxyquire. In particular proxyquire:
There is another Babel plugin, babel-plugin-rewire which aims to solve the same problem, but it generates a large amount of extra code in each module which can cause problems in large production apps.
A technique which doesn't involve any plugins at all is to monkey-patch the exports of a module that you want to mock during a test. The problem with this is that all modules which depend on that module will see the mocks, not just the module you are testing. This can also cause surprising failures.
The Jest test runner has built-in support for mocking modules. If you are using that test runner, you probably want to use its built-in facilities. This plugin works with any project that uses Babel to transpile code, even if Babel is only used in development.
[1.2.0] - 2019-04-11
FAQs
Babel plugin for mocking ES imports
The npm package babel-plugin-mockable-imports receives a total of 8,688 weekly downloads. As such, babel-plugin-mockable-imports popularity was classified as popular.
We found that babel-plugin-mockable-imports demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.