Mocking of Node.js EcmaScript Modules, similar to mock-require.

npm i mock-import -D
Loaders used to get things working, so you need to run tests with:
node --import mock-import/register test.js
mock-import uses transformSource hook, which replaces on the fly all imports with constants declaration:
const {readFile} = globalThis.__mockImportCache.get('fs/promises');
mockImport adds new entry into Map, stopAll clears all mocks and reImport imports file again with new mocks applied.
/* ✅ */
import fs from 'node:fs/promises';
/* ✅ */
import {readFile} from 'node:fs/promises';
/* ✅ */
import * as fs1 from 'node:fs/promises';
/* ✅ */
const {writeFile} = await import('fs/promses');
/* ✅ */
export * as fs2 from 'fs/promises';/* ✅ */
/* ❌ */
export * from 'fs/promises';
// doesn't have syntax equivalent
mock-import works?As was said before, loaders used to get things working. This is experimental technology,
but most likely it wan't change. If it will mock-import will be adapted according to node.js API.
loader hook intercepts into import process and get pathname of imported file;
if pathname in reImports it is processed with 🐊Putout code transformer, changes all import calls to access to __mockImportsCache which is a Map filled with data set by mockImport call. And appends sourcemap at the end, so node can generate correct code coverage.
-import glob from 'glob';
+const glob = global.__mockImportCache.get('./glob.js');
traceCache contains pathname it calls are traced with estrace;Code like this
const f = () => {};
will be changed to
const f = () => {
try {
__estrace.enter('<anonymous:1>', 'trace.js:1', arguments);
} finally {
__estrace.exit('<anonymous:1>', 'trace.js:1');
}
};
Straight after loading and passed to traceImport stack will be filled with data this way:
__estrace.enter = (name, url, args) => stack.push([name, url, Array.from(args)]);
And when the work is done stack will contain all function calls.
traceCache contains some paths current file will be checked for traced imports and change them to form ${path}?count=${count} to re-import them;mock-import supports a couple env variables that extend functionality:
MOCK_IMPORT_NESTED - transform each import statement so mock of module work in nested imports as well (slowdown tests a bit)mockImport(name, mock)name: string - module name;mock: object - mock data;Mock import of a module.
stopAll()Stop all mocks.
reImport(name)name: string - name of a moduleFresh import of a module.
traceImport(name, {stack})name: string name of a modulestack: [fn, url, args];Add tracing of a module.
reTrace(name)name: string - name of traced moduleApply tracing.
enableNestedImports()Enable nested imports, can slowdown tests;
disableNestedImports()Disable nested imports, use when you do not need nested imports support;
Let's suppose you have cat.js:
import {readFile} from 'node:fs/promises';
export default async function cat() {
const readme = await readFile('./README.md', 'utf8');
return readme;
}
You can test it with 📼Supertape:
import {createMockImport} from 'mock-import';
import {test, stub} from 'supertape';
const {
mockImport,
reImport,
stopAll,
} = createMockImport(import.meta.url);
test('cat: should call readFile', async (t) => {
const readFile = stub();
mockImport('fs/promises', {
readFile,
});
const cat = await reImport('./cat.js');
await cat();
stopAll();
t.calledWith(readFile, ['./README.md', 'utf8']);
t.end();
});
Now let's trace it:
import {createMockImport} from 'mock-import';
import {test, stub} from 'supertape';
const {
mockImport,
reImport,
stopAll,
} = createMockImport(import.meta.url);
test('cat: should call readFile', async (t) => {
const stack = [];
traceImport('fs/promises', {
stack,
});
const cat = await reImport('./cat.js');
await cat();
stopAll();
const expected = [
['parse', 'parser.js:3', [
'const a = 5',
]],
['tokenize', 'tokenizer.js:1', [
'parser call',
'const a = 5',
]],
];
t.deepEqual(stack, expected);
t.end();
});
MIT
JavaScript
98.4%
TypeScript
1.6%
Mocking of Node.js EcmaScript Modules, similar to mock-require.

npm i mock-import -D
Loaders used to get things working, so you need to run tests with:
node --import mock-import/register test.js
mock-import uses transformSource hook, which replaces on the fly all imports with constants declaration:
const {readFile} = globalThis.__mockImportCache.get('fs/promises');
mockImport adds new entry into Map, stopAll clears all mocks and reImport imports file again with new mocks applied.
/* ✅ */
import fs from 'node:fs/promises';
/* ✅ */
import {readFile} from 'node:fs/promises';
/* ✅ */
import * as fs1 from 'node:fs/promises';
/* ✅ */
const {writeFile} = await import('fs/promses');
/* ✅ */
export * as fs2 from 'fs/promises';/* ✅ */
/* ❌ */
export * from 'fs/promises';
// doesn't have syntax equivalent
mock-import works?As was said before, loaders used to get things working. This is experimental technology,
but most likely it wan't change. If it will mock-import will be adapted according to node.js API.
loader hook intercepts into import process and get pathname of imported file;
if pathname in reImports it is processed with 🐊Putout code transformer, changes all import calls to access to __mockImportsCache which is a Map filled with data set by mockImport call. And appends sourcemap at the end, so node can generate correct code coverage.
-import glob from 'glob';
+const glob = global.__mockImportCache.get('./glob.js');
traceCache contains pathname it calls are traced with estrace;Code like this
const f = () => {};
will be changed to
const f = () => {
try {
__estrace.enter('<anonymous:1>', 'trace.js:1', arguments);
} finally {
__estrace.exit('<anonymous:1>', 'trace.js:1');
}
};
Straight after loading and passed to traceImport stack will be filled with data this way:
__estrace.enter = (name, url, args) => stack.push([name, url, Array.from(args)]);
And when the work is done stack will contain all function calls.
traceCache contains some paths current file will be checked for traced imports and change them to form ${path}?count=${count} to re-import them;mock-import supports a couple env variables that extend functionality:
MOCK_IMPORT_NESTED - transform each import statement so mock of module work in nested imports as well (slowdown tests a bit)mockImport(name, mock)name: string - module name;mock: object - mock data;Mock import of a module.
stopAll()Stop all mocks.
reImport(name)name: string - name of a moduleFresh import of a module.
traceImport(name, {stack})name: string name of a modulestack: [fn, url, args];Add tracing of a module.
reTrace(name)name: string - name of traced moduleApply tracing.
enableNestedImports()Enable nested imports, can slowdown tests;
disableNestedImports()Disable nested imports, use when you do not need nested imports support;
Let's suppose you have cat.js:
import {readFile} from 'node:fs/promises';
export default async function cat() {
const readme = await readFile('./README.md', 'utf8');
return readme;
}
You can test it with 📼Supertape:
import {createMockImport} from 'mock-import';
import {test, stub} from 'supertape';
const {
mockImport,
reImport,
stopAll,
} = createMockImport(import.meta.url);
test('cat: should call readFile', async (t) => {
const readFile = stub();
mockImport('fs/promises', {
readFile,
});
const cat = await reImport('./cat.js');
await cat();
stopAll();
t.calledWith(readFile, ['./README.md', 'utf8']);
t.end();
});
Now let's trace it:
import {createMockImport} from 'mock-import';
import {test, stub} from 'supertape';
const {
mockImport,
reImport,
stopAll,
} = createMockImport(import.meta.url);
test('cat: should call readFile', async (t) => {
const stack = [];
traceImport('fs/promises', {
stack,
});
const cat = await reImport('./cat.js');
await cat();
stopAll();
const expected = [
['parse', 'parser.js:3', [
'const a = 5',
]],
['tokenize', 'tokenizer.js:1', [
'parser call',
'const a = 5',
]],
];
t.deepEqual(stack, expected);
t.end();
});
MIT
JavaScript
98.4%
TypeScript
1.6%