@node-loaders/mock
Loader that mocks imported/required modules.
Features
- ESM support
- CJS support
- Mixed CJS/ESM support
- Framework agnostic
- Typescript support (using a typescript loader, except CJS)
- Delegates the actual module import to the chain, keeping compatibility with others loaders (like typescript)
- No cache manipulation is necessary, no drawbacks like why-is-proxyquire-messing-with-my-require-cache
- Compatible with Typescript's
esModuleInterop
- Full or partial mock support
- Unused mock detection
- Configurable mocked module import deep using
maxDepth
(symbol) property. - Trivial to use
Design
Mock uses an unique approach.
Importing using mock will create an alternative version of the module using mocked file names.
ESM supportes url, mock adds necessary metadata to the url search parameters. CJS uses absolute paths, the metadata is stored and passed using a uuid appended to the file path folowing a .mock
extension.
The mocked files will continue until maxDepth
is reached (-1 by default, deep import).
Prior art: proxyquire, esmock.
Usage
For configuration tools, refer to usage
When using in combination with others @node-loaders modules make sure to use @node-loaders/auto for better interoperability .
Loaders
@node-loaders/mock
provides esm(import) and commonjs(require) mocking.@node-loaders/mock/esm
provides esm(import) mocking only.
ESM
Importing a module with mocked dependencies:
import { importMock, checkMocks } from '@node-loaders/mock';
describe(() => {
afterAll(() => {
checkMocks();
});
it(async () => {
const defaultSpy = spy();
const joinSpy = spy();
const mockedModule = await importMock('./module.js', {
'../src/path.js': {
default: defaultSpy,
join: joinSpy,
},
});
expect(joinSpy).toBeCalled();
expect(defaultSpy).toBeCalled();
});
});
Full mocks:
import { importMock, checkMocks, fullMock } from '@node-loaders/mock';
describe(() => {
afterAll(() => {
checkMocks();
});
it(async () => {
const joinSpy = spy();
const mockedModule = await importMock('./module.js', {
'../src/path.js': {
[fullMock]: true,
join: joinSpy,
},
});
});
});
Mock check:
import { importMock, checkMocks, checkMock, ignoreUnused } from '@node-loaders/mock';
describe(() => {
afterAll(() => {
checkMocks();
});
it(async () => {
const joinSpy = spy();
const resolveSpy = spy();
const mockedModule = await importMock('./module.js', {
'../src/path.js': {
join: joinSpy,
},
'node:path': {
[ignoreUnused]: true,
resolve: resolveSpy,
},
});
checkMock(mockedModule);
});
});
Max depth:
import { importMock, checkMocks, maxDepth } from '@node-loaders/mock';
describe(() => {
afterAll(() => {
checkMocks();
});
it(async () => {
const joinSpy = spy();
const resolveSpy = spy();
const mockedModule = await importMock('./module.js', {
'../src/path.js': {
join: joinSpy,
},
'node:path': {
[maxDepth]: -1,
resolve: resolveSpy,
},
});
});
});
CommonJS
From an esm module:
import { mockRequire, checkMocks } from '@node-loaders/mock';
describe(() => {
afterAll(() => {
checkMocks();
});
it(() => {
const defaultSpy = spy();
const joinSpy = spy();
const mockedModule = mockRequire('./module.cjs', {
'../src/path.cjs': {
default: defaultSpy,
join: joinSpy,
},
});
expect(joinSpy).toBeCalled();
expect(defaultSpy).toBeCalled();
});
});
From a cjs module:
const { mockRequire } = require('@node-loaders/mock/require');
describe(() => {
afterAll(() => {
checkMocks();
});
it(() => {
const defaultSpy = spy();
const joinSpy = spy();
const mockedModule = mockRequire('./module.cjs', {
'../src/path.cjs': {
default: defaultSpy,
join: joinSpy,
},
});
expect(joinSpy).toBeCalled();
expect(defaultSpy).toBeCalled();
});
});
Known issues
CommonJS (require) mocking doesn't mock typescript modules.
License
MIT