跳至主要内容
版本:29.7

ECMAScript 模块

注意

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

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

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

在发出警告后,以下是如何在测试中激活 ESM 支持。

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

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

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

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

    如果您的代码库包含来自 *.wasm 文件的 ESM 导入,则您不需要将 --experimental-wasm-modules 传递给 node。Jest 中 WebAssembly 导入的当前实现依赖于实验性的 VM 模块,但是,这在将来可能会改变。

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

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

ESM 和 CommonJS 之间的差异

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

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

jest.useFakeTimers();

// etc.

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

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

ESM 中的模块模拟

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

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

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

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。请参见下面的示例

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.