JSPM

  • Created
  • Published
  • Downloads 35367
  • Score
    100M100P100Q146579F
  • License MIT

mock esm modules for unit-tests

Package Exports

  • esmock
  • esmock/src/esmockLoader.mjs

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (esmock) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

esmock

npm version Build Status

esmock provides simple, native ESM import mocking on a per-unit basis.

Quick Start

To get started, simply install esmock, and update your test script to include it as a loader.

Note: esmock must be used with node's experimental --loader

  1. Install esmock:
$ npm i -D esmock
  1. Update the test script in your package.json:
{
  "type": "module",
  "scripts": {
    "test-ava": "ava --node-arguments=\"--loader=esmock\"",
    "test-mocha": "mocha --loader=esmock --no-warnings"
  }
}

Mocking ESM Imports inside Unit Tests

Mocking is very simple and can be done on a per-unit basis. This means that each unit in your test file can be mocked differently, to allow for mocking out various scenarios.

The syntax is very simple:

const targetModuleExports = await esmock(targetModule, childMocks, globalMocks)
  • targetModule: The path to the module you'll be testing.
  • targetModuleExports: Anything that targetModule exports.
  • childMocks: Modules imported into targetModule that you want to mock.
  • globalMocks: Optional Modules that you always want to mock, even if they're not directly imported by targetModule.

The targetModuleExports is the default export, or it can be destructured to retrieve named exports.

// Grabbing the default export
const defaultExport = await esmock('../src/my-module.js', childMocks, globalMocks)
// Grabbing both the default export and a named export
const { default: defaultExport, namedExport } = await esmock('../src/my-module.js', childMocks, globalMocks)

The *mocks parameters follow the below syntax:

childMocks | globalMocks = {
  'npm-pkg-name': {
    default: __mock_value__,
    namedExport: __mock_value__
  },
  '../relative/path/to/imported/file.js': {
    default: __mock_value__,
    namedExport: __mock_value__
  }
}

Where __mock_value__ could be a string, function, class, or anything else, depending on the module/file you're importing in your target.

Example

Here's an example that demonstrates mocking a named module, as well as a local JS file. In this example, we're testing the ./src/main.js and mocking the modules and files that it imports.

./src/main.js:

import serializer from 'serializepkg';
import someDefaultExport from './someModule.js';

export default () => {
  const json = serializer(someDefaultExport());
  return json;
}

./tests/main.js:

test('should mock modules and local files at same time', async t => {
  const main = await esmock('../src/main.js', {
    serializepkg: {
      default: obj => JSON.stringify(obj)
    },
    '../src/someModule.js' : {
      default: () => ({ foo: 'bar' })
    }
  });

  // Because `serializepkg` is mocked as a function that calls JSON.stringify()
  // And `someDefaultExport` is mocked as a function that returns { foo: 'bar' }
  t.is(main(), JSON.stringify({ foo: 'bar' }));
});

Examle, mocking await import( 'modulename' )

When esmock loads and returns a module it deletes mocking definitions from a cache by default. Disable this behaviour when using 'await import', so that mocked definitions can be loaded during test runtime. A function can be called later to clear the cache.

export default async function usesAwaitImport (config) {
  const eslint = (await import('eslint'));

  return new eslint.ESLint({ baseConfig : config });
};

In the test file, define an 'options' object before local and global mock definitions, { isPurge: false },

test('mocks inline `async import("name")`', async t => {
  const writeJSConfigFile = await esmock('./local/usesAwaitImport.mjs', {
    isPurge : false
  }, {
    eslint : {
      ESLint : function (o) {
        this.stringify = () => JSON.stringify(o);

        return this;
      }
    }
  });

  t.is((await writeJSConfigFile('config')).stringify(), JSON.stringify({
    baseConfig : 'config'
  }));

  // clear the cache
  esmock.purge(writeJSConfigFile);
});

If there are not many tests or if each test completes in a separate process, skipping esmock.purge() is OK

changelog

  • 1.2.0 Nov.26.2021
    • add support for await import
  • 1.1.0 Nov.25.2021
    • add windows-latest to testing pipeline and begin windows support
    • removed files and functions no longer needed
    • increment resolvewithplus package and other dependencies
  • 1.0.1 Nov.02.2021
    • add node v17.x to testing pipeline
    • add, make warning message go away for node 16.12.0+
  • 1.0.0 Oct.27.2021
    • release version 1.0
  • 0.4.2 Oct.27.2021
    • export 'load' hook from moduleLoader, required by node v16.12.0+
  • 0.4.1 Oct.10.2021
    • version bump, increment devDependencies,
    • major improvement to README, thanks @swivelgames
  • 0.4.0 Sep.07.2021
    • do not runtime error when returning type '[object Module]' default
  • 0.3.9 May.05.2021
    • small change to README
    • added a test, update gitlab action to use node 16.x
  • 0.3.8 Apr.21.2021
    • small change to README
  • 0.3.7 Apr.20.2021
    • add test, throw error if mocked module path is not found
  • 0.3.6 Apr.19.2021
    • throw error if mocked module path is not found
  • 0.3.5 Apr.18.2021
    • added gitlab actions npm test: node 12.x, 14.x and 15.x
  • 0.3.3 Apr.13.2021
    • added keywords to package.json, use github action to npm publish
  • 0.3.1 Apr.12.2021
    • simplify README
  • 0.3.0 Apr.10.2021
    • adds support for mocking modules 'globally' for the instance
  • 0.2.0 Apr.10.2021
    • adds support for mocking core modules such as fs and path
  • 0.1.0 Apr.10.2021
    • adds support for native esm modules