4.0.3 • Published 4 months ago

mock-import v4.0.3

Weekly downloads
22
License
MIT
Repository
github
Last release
4 months ago

Mock Import License NPM version Build Status Coverage Status

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

Recording 2022-06-16 at 19 41 04

Install

npm i mock-import -D

Run

Loaders used to get things working, so you need to run tests with:

node --loader mock-import 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.

Supported Declarations

/* ✅ */
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';

Unsupported Declarations

/* ❌ */
export * from 'fs/promises';
// doesn't have syntax equivalent

How 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');
  • if 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;

Environment variables

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)

API

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 module

Fresh import of a module.

traceImport(name, {stack})

  • name: string name of a module
  • stack: [fn, url, args];

Add tracing of a module.

reTrace(name)

  • name: string - name of traced module

Apply tracing.

enableNestedImports()

Enable nested imports, can slowdown tests;

disableNestedImports()

Disable nested imports, use when you do not need nested imports support;

Example

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();
});

License

MIT

4.0.3

4 months ago

4.0.2

5 months ago

4.0.1

5 months ago

4.0.0

5 months ago

3.4.1

5 months ago

3.4.0

8 months ago

3.2.1

9 months ago

3.2.0

10 months ago

3.3.1

9 months ago

3.3.0

9 months ago

3.1.2

11 months ago

3.1.1

11 months ago

3.1.0

11 months ago

3.3.2

9 months ago

3.0.6

1 year ago

3.0.5

1 year ago

3.0.4

2 years ago

3.0.3

2 years ago

3.0.2

2 years ago

3.0.1

2 years ago

2.11.0

2 years ago

2.2.1

2 years ago

2.11.1

2 years ago

2.2.0

2 years ago

2.4.0

2 years ago

2.2.2

2 years ago

2.6.0

2 years ago

2.8.0

2 years ago

2.7.4

2 years ago

2.7.3

2 years ago

2.7.6

2 years ago

2.7.5

2 years ago

3.0.0

2 years ago

2.7.8

2 years ago

2.7.7

2 years ago

2.3.0

2 years ago

2.12.0

2 years ago

2.5.0

2 years ago

2.10.0

2 years ago

2.7.0

2 years ago

2.9.0

2 years ago

2.7.2

2 years ago

2.7.1

2 years ago

2.1.3

2 years ago

2.1.2

3 years ago

2.1.1

3 years ago

2.1.0

3 years ago

2.0.0

3 years ago

1.12.3

3 years ago

1.12.2

3 years ago

1.12.1

3 years ago

1.12.0

3 years ago

1.11.0

3 years ago

1.10.0

3 years ago

1.9.0

3 years ago

1.8.0

3 years ago

1.7.1

3 years ago

1.7.0

3 years ago

1.6.0

3 years ago

1.5.0

3 years ago

1.4.1

3 years ago

1.4.0

3 years ago

1.3.0

3 years ago

1.2.0

3 years ago

1.1.0

3 years ago

1.0.8

3 years ago

1.0.7

3 years ago

1.0.6

3 years ago

1.0.5

3 years ago

1.0.4

3 years ago

1.0.3

3 years ago

1.0.2

3 years ago

1.0.1

3 years ago

1.0.0

3 years ago