Skip to main content
Version: 29.7

ECMAScript 模块

提醒

Jest 附带了对 ECMAScript 模块 (ESM) 的实验性支持。

¥Jest ships with experimental support for ECMAScript Modules (ESM).

该实现可能存在错误并且缺乏功能。有关最新状态,请查看问题跟踪器上的 issuelabel

¥The implementation may have bugs and lack features. For the latest status check out the issue and the label on the issue tracker.

另请注意,Jest 用于实现 ESM 支持的 API 仍然是 Node 认为是实验性的(截至版本 18.8.0)。

¥Also note that the APIs Jest uses to implement ESM support are still considered experimental by Node (as of version 18.8.0).

消除警告后,这就是你在测试中激活 ESM 支持的方法。

¥With the warnings out of the way, this is how you activate ESM support in your tests.

  1. 确保你通过传递 transform: {} 来禁用 代码转换,或者以其他方式配置你的转换器以触发 ESM 而不是默认的 CommonJS (CJS)。

    ¥Ensure you either disable code transforms by passing transform: {} or otherwise configure your transformer to emit ESM rather than the default CommonJS (CJS).

  2. 执行 node--experimental-vm-modules,例如 node --experimental-vm-modules node_modules/jest/bin/jest.jsNODE_OPTIONS="$NODE_OPTIONS --experimental-vm-modules" npx jest

    ¥Execute node with --experimental-vm-modules, e.g. node --experimental-vm-modules node_modules/jest/bin/jest.js or NODE_OPTIONS="$NODE_OPTIONS --experimental-vm-modules" npx jest etc.

    在 Windows 上,你可以使用 cross-env 来设置环境变量。

    ¥On Windows, you can use cross-env to be able to set environment variables.

    如果使用 Yarn,则可以使用 yarn node --experimental-vm-modules $(yarn bin jest)。如果你使用 Yarn Plug'n'Play,此命令也将起作用。

    ¥If you use Yarn, you can use yarn node --experimental-vm-modules $(yarn bin jest). This command will also work if you use Yarn Plug'n'Play.

    如果你的代码库包含从 *.wasm 文件的 ESM 导入,则无需将 --experimental-wasm-modules 传递到 node。目前 Jest 中 WebAssembly 导入的实现依赖于实验性 VM 模块,但是,这种情况将来可能会发生变化。

    ¥If your codebase includes ESM imports from *.wasm files, you do not need to pass --experimental-wasm-modules to node. Current implementation of WebAssembly imports in Jest relies on experimental VM modules, however, this may change in the future.

  3. 除此之外,我们尝试遵循 node 的逻辑来激活 "ESM 模式"(例如在 package.json.mjs 文件中查看 type),详细信息请参见 他们的文档

    ¥Beyond that, we attempt to follow node's logic for activating "ESM mode" (such as looking at type in package.json or .mjs files), see their docs for details.

  4. 如果你想将其他文件扩展名(例如 .jsx.ts)视为 ESM,请使用 extensionsToTreatAsEsm 选项

    ¥If you want to treat other file extensions (such as .jsx or .ts) as ESM, please use the extensionsToTreatAsEsm option.

ESM 和 CommonJS 之间的差异

¥Differences between ESM and CommonJS

大多数差异都在 Node 的文档 中进行了解释,但除了那里提到的内容之外,Jest 还向所有执行的文件中注入了一个特殊变量 - jest 对象。要在 ESM 中访问该对象,你需要从 @jest/globals 模块导入它或使用 import.meta

¥Most of the differences are explained in Node's documentation, but in addition to the things mentioned there, Jest injects a special variable into all executed files - the jest object. To access this object in ESM, you need to import it from the @jest/globals module or use import.meta.

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

jest.useFakeTimers();

// etc.

// alternatively
import.meta.jest.useFakeTimers();

// jest === import.meta.jest => true

ESM 中的模块模拟

¥Module mocking in ESM

由于 ESM 在查看代码之前评估静态 import 语句,因此 CJS 中发生的 jest.mock 调用提升对于 ESM 不起作用。要在 ESM 中模拟模块,你需要在 jest.mock 调用后使用 require 或动态 import() 来加载模拟模块 - 这同样适用于加载模拟模块的模块。

¥Since ESM evaluates static import statements before looking at the code, the hoisting of jest.mock calls that happens in CJS won't work for ESM. To mock modules in ESM, you need to use require or dynamic import() after jest.mock calls to load the mocked modules - the same applies to modules which load the mocked modules.

通过 jest.unstable_mockModule 支持 ESM 模拟。顾名思义,该 API 仍在开发中,请关注 这个问题 获取更新。

¥ESM mocking is supported through jest.unstable_mockModule. As the name suggests, this API is still work in progress, please follow this issue for updates.

jest.unstable_mockModule 的用法与 jest.mock 基本相同,但有两点不同:工厂函数是必需的,它可以是同步的或异步的:

¥The usage of jest.unstable_mockModule is essentially the same as jest.mock with two differences: the factory function is required and it can be sync or async:

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

jest.unstable_mockModule('node:child_process', () => ({
execSync: jest.fn(),
// etc.
}));

const {execSync} = await import('node:child_process');

// etc.

对于模拟 CJS 模块,你应该继续使用 jest.mock。请参阅下面的示例:

¥For mocking CJS modules, you should continue to use jest.mock. See the example below:

main.cjs
const {BrowserWindow, app} = require('electron');

// etc.

module.exports = {example};
main.test.cjs
import {createRequire} from 'node:module';
import {jest} from '@jest/globals';

const require = createRequire(import.meta.url);

jest.mock('electron', () => ({
app: {
on: jest.fn(),
whenReady: jest.fn(() => Promise.resolve()),
},
BrowserWindow: jest.fn().mockImplementation(() => ({
// partial mocks.
})),
}));

const {BrowserWindow} = require('electron');
const exported = require('./main.cjs');

// alternatively
const {BrowserWindow} = (await import('electron')).default;
const exported = await import('./main.cjs');

// etc.