ECMAScript 模块
Jest 带有对 ECMAScript 模块 (ESM) 的 **实验性** 支持。
该实现可能存在错误且缺少功能。有关最新状态,请查看 问题 和 标签 在问题跟踪器上。
另请注意,Jest 用于实现 ESM 支持的 API 仍然 被 Node 视为实验性(截至版本 18.8.0
)。
在发出警告后,以下是如何在测试中激活 ESM 支持。
-
确保您通过传递
transform: {}
禁用 代码转换,或者以其他方式配置您的转换器以发出 ESM 而不是默认的 CommonJS (CJS)。 -
使用
--experimental-vm-modules
执行node
,例如node --experimental-vm-modules node_modules/jest/bin/jest.js
或NODE_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 模块,但是,这在将来可能会改变。 -
除此之外,我们尝试遵循
node
的逻辑来激活“ESM 模式”(例如查看package.json
中的type
或.mjs
文件),请参阅 他们的文档 以了解详细信息。 -
如果您想将其他文件扩展名(例如
.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
。请参见下面的示例
const {BrowserWindow, app} = require('electron');
// etc.
module.exports = {example};
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.