Testing ECMAScript Modules Natively in Jest

on

In this tutorial, we’ll setup Jest to test a Node.js application that uses ECMAScript modules (ESM). Recent versions of Node.js have full support for ESM, which will eventually become the default mode in place of CommonJS. In the meantime, ESM mode can be easily enabled by setting "type": "module" in package.json.

Jest and ESM

Typically, Jest relies on Babel to transpile ESM code to CommonJS before running any tests, but Babel just adds another layer of complexity. Jest already has built-in support for ESM, though currently at an experimental stage.

There are a few key differences from testing CommonJS modules:

  1. We need to pass the --experimental-vm-modules flag to node when running Jest
  2. Instead of jest.mock we have to use jest.unstable_mockModule
  3. The jest object has to be imported from @jest/globals
  4. We have to use dynamic imports to load our mocked modules in the right place

Testing a class

Let’s assume we are exporting the following class from myClass.js.

export class MyClass {
  async get() {
    return Promise.resolve("Hello World!");
  }
}

In our test, myClass.test.js, we could create a mock of our MyClass class like so:

import { jest } from "@jest/globals";

const mockedGet = jest.fn().mockResolvedValue("Mocked");

jest.unstable_mockModule("./myClass.js", () => {
  return {
    MyClass: jest.fn(() => ({
      get: mockedGet,
    })),
  };
});

// ESM import statements are evaluated first, so we need
// to use a dynamic import() to make sure our mocked module
// is loaded after jest.unstable_mockModule()
const { MyClass } = await import("./myClass.js");

const myInstance = new MyClass();

describe("MyClass", () => {
  beforeEach(() => {
    mockedGet.mockClear();
  });

  it("should work", async () => {
    const result = await myInstance.get();
    expect(result).toEqual("Mocked");
  });
});

To run our test, we need to pass the --experimental-vm-modules flag to node. We can add the command to our package.json.

...
"type": "module",
"scripts": {
  "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
}
...

For more information on using Jest with ECMAScript modules, you can have a look at the official docs.