Mocking of Node.js EcmaScript Modules, similar to mock-require.
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} = global.__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 {readFile as readFile1} from 'fs/promises';
/* ❌ */ export * from 'fs/promises'; // doesn't have syntax equivalent
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
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)name: string
- module name;mock: object
- mock data;Mock import
of a module
.
Stop all mocks.
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.
name: string
- name of traced moduleApply tracing.
Enable nested imports, can slowdown tests;
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
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4