在 webpack 项目中使用 Jest:配置迁移、静态资源 Mock 与模块解析全指南

📅 发布时间:2026/9/19 23:03:22
在 webpack 项目中使用 Jest:配置迁移、静态资源 Mock 与模块解析全指南
在 webpack 项目中使用 Jest配置迁移、静态资源 Mock 与模块解析全指南【免费下载链接】jestDelightful JavaScript Testing.项目地址: https://gitcode.com/gh_mirrors/je/jestJest 完全可以与使用 webpack 管理资源、样式与编译流程的项目协同工作——本指南将完整演示如何把一份典型的 webpack 配置loader、asset 规则、resolve别名与目录逐项翻译为等价的 Jest 配置并深入讲解moduleNameMapper、moduleDirectories、moduleFileExtensions、modulePaths与自定义transform的底层实现。读完本文你将掌握在 webpack 工程中落地 Jest、优雅处理样式与图片等静态资源、以及正确配置模块查找路径的完整实战方案。本文基于 Jest 30 官方文档 Webpack.md与 docs/Webpack.md 内容一致整理并扩充。为什么 webpack 项目集成 Jest 会“特殊”webpack 与其他工具相比之所以给测试带来独特挑战在于它深度集成了应用本身它负责管理样式表、图片和字体等资源并支撑起庞大的编译到 JavaScript语言与工具生态。而 Jest 默认的模块解析与文件加载机制并不认识 CSS、图片等资源因此需要一套显式的配置来翻译webpack 的能力。好消息是Jest 的大部分配置项都能与 webpack 的对应能力一一映射。核心思路是用transform处理需要编译的 JavaScript/TypeScript 代码默认走babel-jest用moduleNameMapper把样式、图片等资源文件替换为 Mock 模块用moduleDirectories、moduleFileExtensions、modulePaths复刻 webpack 的模块查找逻辑用moduleNameMapper的正则映射复刻 webpack 的resolve.alias。一个典型的 webpack 配置示例下面是一份常见的 webpack 配置它同时处理 JS/JSX 编译、CSS 样式、内联图片与字体资源并配置了路径别名与自定义查找目录module.exports { module: { rules: [ { test: /\.jsx?$/, exclude: [node_modules], use: [babel-loader], }, { test: /\.css$/, use: [style-loader, css-loader], }, { test: /\.gif$/, type: asset/inline, }, { test: /\.(ttf|eot|svg)$/, type: asset/resource, }, ], }, resolve: { alias: { config$: ./configs/app-config.js, react: ./vendor/react-master, }, extensions: [.js, .jsx], modules: [ node_modules, bower_components, shared, /shared/vendor/modules, ], }, };这份配置中的每个部分在 Jest 中都有对应的落点webpack 配置职责Jest 对应配置module.rulesbabel-loader编译 JS/JSXtransformbabel-jestmodule.rulescss/asset加载样式与资源moduleNameMapper或transformresolve.extensions可省略扩展名的文件后缀moduleFileExtensionsresolve.modules模块查找目录moduleDirectoriesmodulePathsresolve.alias模块路径别名moduleNameMapper正则映射如果项目中的 JavaScript 文件由 Babel 转换可安装babel-jest插件来启用 Babel 支持参见 GettingStarted.md非 Babel 的 JavaScript 转换则可用 Jest 的transform配置项处理。babel-jest 到底做了什么babel-jest是 Jest 官方提供的 Babel 转换器实现位于 packages/babel-jest/src/index.ts。从源码可见几个关键事实它通过createTransformer工厂创建符合 Jesttransform接口的转换器实现了process/processAsync同步/异步编译与getCacheKey/getCacheKeyAsync缓存键计算默认会把babel-preset-jest追加进 Babel 的 presetspresets: [...(inputOptions.presets ?? []), ...(excludeJestPreset true ? [] : [jestPresetPath])]该 preset 负责jest.mock等调用的提升hoisting——因此文档特别提示如果显式声明excludeJestPreset: true会破坏jest.mock的提升机制生成的缓存键cache key会综合 Babel 配置、源码内容、相对 rootDir 的路径、instrument标志、NODE_ENV/BABEL_ENV与 Node 版本等计算见 getCacheKeyFromConfig。这就是修改了.babelrc之后需要jest --clearCache的底层原因——Babel 配置变化会影响缓存键但旧缓存可能未失效。babel-jest的 READMEpackages/babel-jest/README.md给出了最简洁的接入方式安装babel-jest后它会自动用 Babel 编译 JavaScript只有当你需要同时使用多个代码预处理器时才需要显式在transform中声明它transform: { \\.[jt]sx?$: babel-jest },还可以向babel-jest传递额外的 Babel 选项例如transform: { \\.[jt]sx?$: [babel-jest, { extends: ./babel.config.js, plugins: [babel-plugin-transform-import-meta] }] }处理静态资源Handling Static Assetswebpack 能把 CSS、图片、字体等资源打包进应用但这些文件对单元测试没有实际价值因此标准做法是把它们 Mock 掉。通过moduleNameMapper可以把匹配到的资源扩展名替换成指定的 Mock 模块module.exports { moduleNameMapper: { \\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$: rootDir/__mocks__/fileMock.js, \\.(css|less)$: rootDir/__mocks__/styleMock.js, }, };对应的两个 Mock 文件内容极简module.exports {};module.exports test-file-stub;即样式模块返回空对象因为测试中不关心样式图片/字体等文件模块返回一个占位字符串test-file-stub。你可以根据 webpack 配置实际处理的文件类型自由调整这里的正则表达式。Mocking CSS Modules如果项目使用 CSS Modules直接返回空对象会导致styles.foobar为undefined。更优雅的方案是使用 ES6 Proxy 库identity-obj-proxy来 Mock CSS Modules安装方式npm install --save-dev identity-obj-proxy然后在moduleNameMapper中把样式映射到该代理库module.exports { moduleNameMapper: { \\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$: rootDir/__mocks__/fileMock.js, \\.(css|less)$: identity-obj-proxy, }, };这样样式对象上的所有 className 查询都会原样返回例如styles.foobar foobar。这对 React 的 Snapshot Testing快照测试非常有用——组件快照中可以稳定看到类名字符串。用自定义 transformer 处理资源如果moduleNameMapper无法满足需求可以使用 Jest 的transform配置项来指定资源的转换方式。例如下面这个 transformer 返回文件 basename使require(logo.jpg)返回logoconst path require(path); module.exports { process(sourceText, sourcePath, options) { return { code: module.exports ${JSON.stringify(path.basename(sourcePath))};, }; }, };module.exports { moduleNameMapper: { \\.(css|less)$: identity-obj-proxy, }, transform: { \\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$: rootDir/fileTransformer.js, }, };注意此例中process返回的是{ code }形式的对象这也是当前 Jest 版本中转换器标准的返回值形态babel-jest的process同样返回{code, map}见 packages/babel-jest/src/index.ts。:::tip 保留默认 babel-jest 如果要在额外代码预处理器之外继续使用默认的babel-jest请务必显式包含它否则 JS/JSX 代码将不再走 Babel 编译transform: { \\.[jt]sx?$: babel-jest, \\.css$: some-css-transformer }:::配置 Jest 找到我们的文件处理完如何转换文件还需要告诉 Jest到哪里找文件。webpack 的resolve.modules与resolve.extensions在 Jest 中分别有直接的对应项moduleDirectories与moduleFileExtensions。module.exports { moduleFileExtensions: [js, jsx], moduleDirectories: [node_modules, bower_components, shared], moduleNameMapper: { \\.(css|less)$: rootDir/__mocks__/styleMock.js, \\.(gif|ttf|eot|svg)$: rootDir/__mocks__/fileMock.js, }, };moduleFileExtensions模块可省略扩展名的后缀数组对应 webpack 的resolve.extensionsmoduleDirectories从发起 require 的模块所在位置逐级向上递归搜索的目录名数组见 Descriptions.ts对应 webpack 的resolve.modulesmoduleNameMapper资源 Mock 继续生效。:::note 关于rootDirrootDir是 Jest 的特殊令牌运行时会被替换为项目根目录。大多数情况下它就是package.json所在目录除非你在配置中指定了自定义的rootDir。在 jest-config 的归一化逻辑中moduleNameMapper的值会经过_replaceRootDirTags把rootDir替换为真实的options.rootDir见 normalize.tsmodulePaths/roots等数组项也会先replaceRootDirInPath再解析为绝对路径见 normalize.ts。 :::modulePaths对应 webpack 的 resolve.rootswebpack 的resolve.roots设置NODE_PATH的替代方案在 Jest 中的对应项是modulePathsmodule.exports { modulePaths: [/shared/vendor/modules], moduleFileExtensions: [js, jsx], moduleDirectories: [node_modules, bower_components, shared], moduleNameMapper: { \\.(css|less)$: rootDir/__mocks__/styleMock.js, \\.(gif|ttf|eot|svg)$: rootDir/__mocks__/fileMock.js, }, };modulePaths用于追加额外的绝对查找路径如/shared/vendor/modules从源码看它会与roots一起被解析为基于rootDir的绝对路径见 normalize.ts。用 moduleNameMapper 复刻 resolve.alias最后是 webpack 的resolve.alias。Jest 同样通过moduleNameMapper的正则映射来复刻key 是匹配 import/require 路径的正则value 是目标路径。module.exports { modulePaths: [/shared/vendor/modules], moduleFileExtensions: [js, jsx], moduleDirectories: [node_modules, bower_components, shared], moduleNameMapper: { \\.(css|less)$: rootDir/__mocks__/styleMock.js, \\.(gif|ttf|eot|svg)$: rootDir/__mocks__/fileMock.js, ^react(.*)$: rootDir/vendor/react-master$1, ^config$: rootDir/configs/app-config.js, }, };两个关键点^react(.*)$: rootDir/vendor/react-master$1中$1是正则捕获组引用可把react及其子路径如react-dom一并重定向到 vendor 目录等价于 webpack 配置中的react: ./vendor/react-master^config$: rootDir/configs/app-config.js精确匹配config模块等价于 webpack 的config$: ./configs/app-config.js。注意正则写法webpack 用config$的$表示以 config 结尾Jest 的moduleNameMapper则用^config$表示完整匹配 config 字符串二者效果一致但语法习惯不同。从 jest-config 源码看moduleNameMapper的值支持正则 → 模块名或模块名数组的映射并且每个 value 都会做rootDir标签替换见 normalize.ts官方描述为从正则表达式到模块名或模块名数组的映射用于用单个模块 stub 掉资源见 Descriptions.ts。让 Babel 与 Jest 协同preset 与缓存在babel-jest之外如果项目使用 Babel 编译 ES 新语法还需要安装babel/preset-envnpm install --save-dev babel/preset-env然后配置 Babel{ presets: [babel/preset-env] }:::tip 清理缓存 Jest 会缓存文件以加速测试执行。如果你更新了.babelrc而 Jest 表现异常尝试运行jest --clearCache清空缓存。 :::其原理可参考上文对babel-jest缓存键的分析缓存键由 Babel 配置、源码、环境变量等共同决定getCacheKeyFromConfig当.babelrc变更但缓存未失效时就可能出现结果与预期不符的情况。动态 import 的 Babel 配置如果代码使用了动态导入import(some-file.js).then(module ...)需要启用dynamic-import-node插件并配合syntax-dynamic-import语法插件。推荐按环境区分配置只在test环境启用该插件{ presets: [[env, {modules: false}]], plugins: [syntax-dynamic-import], env: { test: { plugins: [dynamic-import-node] } } }这样在开发/构建环境保留原生动态 import 行为而在 Jest 的 Node 测试环境中将其转换为 CommonJS 形式避免 Node 无法直接解析 ESM 动态导入的问题。小结webpack 到 Jest 的配置映射速查表webpack 配置Jest 配置说明module.rules中的 JS 编译transformbabel-jest默认自动启用多预处理器时需显式声明module.rules中的资源加载moduleNameMapper→ Mock 文件样式返回{}文件返回test-file-stubCSS ModulesmoduleNameMapper→identity-obj-proxyclassName 原样返回自定义资源处理transform→ 自定义 transformer例如返回文件 basenameresolve.extensionsmoduleFileExtensions可省略扩展名resolve.modulesmoduleDirectories递归向上查找的目录名resolve.roots/NODE_PATHmodulePaths额外的绝对查找路径resolve.aliasmoduleNameMapper正则映射用^react(.*)$/$1等复刻别名webpack 是一个复杂而灵活的工具针对具体应用的特定需求你可能需要进一步微调配置但对大多数项目而言Jest 的配置体系足以完整承接 webpack 的模块解析与资源处理能力。对于更复杂的 webpack 配置可以进一步研究babel-plugin-webpack-loaders之类的生态工具也可以参考 Jest 30 官方文档中的 Configuration.md 获取每个配置项的完整说明。想在一个 React webpack 项目中亲自上手可参照本文从一份真实 webpack 配置出发逐步翻译出对应的 Jest 配置并运行验证。【免费下载链接】jestDelightful JavaScript Testing.项目地址: https://gitcode.com/gh_mirrors/je/jest创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考