1.1.0 • Published 12 months ago

@node-loaders/mock v1.1.0

Weekly downloads
-
License
-
Repository
-
Last release
12 months ago

@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(); // Detects for unused mocks to avoid mistakes on import changes.
  });

  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(); // Detects for unused mocks to avoid mistakes on import changes.
  });

  it(async () => {
    const joinSpy = spy();

    const mockedModule = await importMock('./module.js', {
      '../src/path.js': {
        [fullMock]: true, // If a name other than 'join' is imported, a 'Module does not provide an export named' is thrown.
        join: joinSpy,
      },
    });
  });
});

Mock check:

import { importMock, checkMocks, checkMock, ignoreUnused } from '@node-loaders/mock';

describe(() => {
  afterAll(() => {
    checkMocks(); // Detects for unused mocks to avoid mistakes on import changes.
  });

  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); // throws error if '../src/path.js' was not imported but not on 'node:path'
  });
});

Max depth:

import { importMock, checkMocks, maxDepth } from '@node-loaders/mock';

describe(() => {
  afterAll(() => {
    checkMocks(); // Detects for unused mocks to avoid mistakes on import changes.
  });

  it(async () => {
    const joinSpy = spy();
    const resolveSpy = spy();

    const mockedModule = await importMock('./module.js', {
      '../src/path.js': {
        join: joinSpy, // Mocked when imported from './module.js' file only
      },
      'node:path': {
        [maxDepth]: -1, // Mocked a configurable deep
        resolve: resolveSpy,
      },
    });
  });
});

CommonJS

From an esm module:

import { mockRequire, checkMocks } from '@node-loaders/mock';

describe(() => {
  afterAll(() => {
    checkMocks(); // Detects for unused mocks to avoid mistakes on import changes.
  });

  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(); // Detects for unused mocks to avoid mistakes on import changes.
  });

  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

1.0.2

12 months ago

1.1.0

12 months ago

1.0.1

12 months ago

1.0.0

12 months ago

0.10.2

1 year ago

0.10.3

1 year ago

0.10.1

1 year ago

0.9.0

1 year ago

0.8.0

1 year ago

0.9.1

1 year ago

0.5.0

1 year ago

0.4.0

1 year ago

0.7.0

1 year ago

0.6.0

1 year ago

0.3.2

1 year ago

0.3.1

1 year ago

0.3.0

1 year ago

0.2.0

1 year ago

0.1.0

1 year ago

0.0.1-alpha.1

1 year ago

0.0.1-alpha.0

1 year ago