Nx 仓库导入后 Jest 测试集成指南:`@nx/jest/plugin`、`jest.preset.js` 与常见问题修复
2026/9/9 23:58:33 网站建设 项目流程

Nx 仓库导入后 Jest 测试集成指南:@nx/jest/pluginjest.preset.js与常见问题修复

【免费下载链接】nxThe Monorepo Platform that amplifies both developers and AI agents. Nx optimizes your builds, scales your CI, and fixes failed PRs automatically. Ship in half the time.项目地址: https://gitcode.com/GitHub_Trending/nx/nx

在使用nx import将外部仓库合并进 Nx workspace 后,Jest 测试往往不会"开箱即用":测试 target 缺失、类型定义无法识别、transform 未配置、甚至package.json里的测试脚本被改坏。本文以 .opencode/skills/nx-import/references/JEST.md 为骨架,结合当前 Nx 仓库中 packages/jest 的插件源码、生成器实现与真实配置文件,系统讲解导入后 Jest 集成的完整链路——从插件推断机制、preset 与 tsconfig 配置,到依赖安装、Jest/Vitest 共存、CI 原子化与最终修复顺序。读完你可以独立完成任意仓库导入后的 Jest 测试修复与 CI 配置。

适用前提:本指南面向使用nx import(参见 .opencode/skills/nx-import/SKILL.md)合并仓库的场景。关于基础的"Jest Preset Missing"修复(创建jest.preset.js并安装依赖),SKILL.md 中有简化说明,本文覆盖更深层的集成问题。

@nx/jest/plugin的工作原理

插件如何推断 test target

@nx/jest/plugin是一个 Nx 推断型插件(inference plugin)。它扫描工作区中符合**/jest.config.{cjs,mjs,js,cts,mts,ts}模式的文件,为每个配置存在的项目创建一个testtarget。这个 glob 定义在 packages/jest/src/plugins/plugin.ts:

const jestConfigGlob = '**/jest.config.{cjs,mjs,js,cts,mts,ts}';

插件在createNodes中完成两件事:

  1. 并行加载所有 Jest 配置(通过loadConfigFile,并优先查找tsconfig.spec.jsontsconfig.test.jsontsconfig.jest.jsontsconfig.json);
  2. 为每个项目构建 targets:默认 target 名为test,执行命令为jest,且会从 Jest 配置里解析出presetsetupFilesmoduleNameMapper等外部引用,自动推导任务inputsoutputs(缓存与增量测试的基础)。

注意插件的项目判定逻辑(checkIfConfigFileShouldBeProject):如果某个jest.config.*所在目录既没有package.json也没有project.json,插件不会为它创建项目;如果配置内容包含getJestProjectsAsync(),也会被跳过——因为该函数依赖项目图,会形成循环依赖。

插件配置选项

nx.json中注册插件时可以传入选项:

{ "plugin": "@nx/jest/plugin", "options": { "targetName": "test" } }

从源码JestPluginOptions(packages/jest/src/plugins/plugin.ts)可以看到完整选项:

选项默认值作用
targetName"test"推断出的测试 target 名称
ciTargetName开启 CI 原子化(每文件一个 target)时使用的聚合 target 名,如test-ci
ciGroupNameciTargetName推导CI 原子化任务在 Nx Cloud 上的分组名
disableJestRuntimefalse是否使用 Nx 自己的配置加载器与测试匹配器替代jest-config/jest-runtime;禁用后更快但可能在边界情况下不如 Jest 自身正确
useJestResolverdisableJestRuntime相反是否用 Jest 的 resolver 解析 preset、transform、setup 文件等引用作为任务 inputs;能跟随 symlink、支持moduleDirectories/modulePaths

normalizeOptionstargetName ??= 'test'(packages/jest/src/plugins/plugin.ts)保证未配置时默认为test

npx nx add @nx/jest到底做了什么

npx nx add @nx/jest会执行 init 生成器(packages/jest/src/generators/init/init.ts),主要做两件事:

  1. nx.json中注册@nx/jest/plugin——没有它,任何testtarget 都不会被推断出来;
  2. 更新namedInputs.production,将测试相关文件从生产文件集排除:
productionFileSet.push( '!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)', '!{projectRoot}/tsconfig.spec.json', '!{projectRoot}/jest.config.[jt]s', '!{projectRoot}/src/test-setup.[jt]s', '!{projectRoot}/test-setup.[jt]s' );

此外,当工作区走显式 executor(@nx/jest:jest)路径时,init 生成器还会通过upsertTargetDefault写入 target 默认值:cache: trueinputsdefault+^production+{workspaceRoot}/jest.preset.*)、options.passWithNoTests: true,以及ci配置(ci: true, codeCoverage: true)。

两个关键陷阱(原文档特别强调):

  • nx add @nx/jest不会创建jest.preset.js。该文件只在运行生成器(如@nx/jest:configuration)时才会生成。对于nx import的场景,你必须手动创建(见下一节)。
  • 反过来,如果你手动创建了jest.preset.js却跳过了npx nx add @nx/jest,插件未注册,nx run PROJECT:test会报Cannot find target 'test'两者缺一不可。

Jest Preset:共享配置的基石

根目录jest.preset.js

preset 提供了共享的 Jest 配置:测试匹配模式、ts-jest transform、resolver、jsdom 环境等。最小写法:

const nxPreset = require('@nx/jest/preset').default; module.exports = { ...nxPreset };

当前 Nx 仓库根目录的真实 jest.preset.js 展示了在nxPreset基础上覆盖更多选项的写法:设置testTimeout、用@swc/jest替换默认的 ts-jest transform、覆盖testEnvironmentnode、配置moduleNameMapper将 ESM-only 的@clack/promptsoraprettier等替换为 scripts/jest-mocks 下的 mock 实现,并通过setupFiles引入 scripts/unit-test-setup.js。这提示你:preset 是 workspace 级约定的最佳落点。

nxPreset的默认值

nxPreset定义在 packages/jest/preset/jest-preset.ts,核心默认值如下:

配置项默认值说明
testMatch**/?(*.)+(spec|test).[jt]s?(x)(Jest 30 起支持?([mc])变体)匹配 spec/test 文件
resolver@nx/jest/plugins/resolverNx 自定义 resolver,处理 monorepo 内跨包解析
moduleFileExtensions['ts', 'js', 'mjs', 'html'](Jest 30 起增加 mts/cts/cjs)支持 TypeScript 与 Angular 模板
coverageReporters['html']覆盖率输出格式
transformts-jesttsconfig: '<rootDir>/tsconfig.spec.json'默认用 ts-jest 转译
testEnvironmentjsdom注意:Nx preset 默认是 jsdom,纯 Node 项目需显式覆盖为node
modulePathIgnorePatterns['<rootDir>/dist/', '<rootDir>/out-tsc/']忽略构建产物
testEnvironmentOptions.customExportConditions['node', 'require', 'default']强制 Jest 加载 CommonJS 代码,规避 ESM 兼容问题

项目级jest.config.ts

每个项目通过相对路径引用根 preset:

export default { displayName: 'my-lib', preset: '../../jest.preset.js', // project-specific overrides };

preset路径是相对项目根目录到 workspace 根目录的。子目录导入时保留原始相对路径(如../../jest.preset.js),只要导入目标目录与源目录深度一致,该相对路径即可正确解析。当前仓库 e2e 测试 e2e/jest/src/jest.test.ts 中生成的配置同样使用了preset: "../../jest.preset.js"["ts-jest", { tsconfig: "<rootDir>/tsconfig.spec.json" }]的 transform,可作为标准样板。

测试依赖安装

核心依赖(始终需要)

pnpm add -wD jest ts-jest @types/jest @nx/jest

-wD表示安装到 workspace 根目录的 devDependencies。Jest 30 起还会依赖unrs-resolver,init 生成器中已通过acknowledgeBuildScripts处理其 postinstall(packages/jest/src/generators/init/init.ts)。

按环境区分

  • DOM 测试(React、Vue、浏览器类库):需要jest-environment-jsdom,因为nxPreset默认testEnvironment: 'jsdom'
  • Node 测试(API、CLI):无需额外依赖。Jest 默认是node环境,但 Nx preset 默认是jsdom——因此 Node 项目要在项目级jest.config.ts中显式覆盖testEnvironment: 'node'(仓库根 jest.preset.js 就是这么做的)。

React 测试

pnpm add -wD @testing-library/react @testing-library/jest-dom

React + Babel(非 ts-jest transform)

部分 React 项目(老 Nx workspace、CRA 迁移项目)用 Babel 而非 ts-jest 做 JSX 转译:

pnpm add -wD babel-jest @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript

何时需要:项目jest.configtransform使用的是babel-jest而不是ts-jest。判断方法就是检查jest.config.*中的 transform 段。

Vue 测试

pnpm add -wD @vue/test-utils

注意:Vue 项目通常使用 Vitest 而非 Jest——详见 .opencode/skills/nx-import/references/VITE.md。

tsconfig.spec.json:测试文件的类型配置

每个 Jest 项目都需要一个包含测试文件的tsconfig.spec.json。标准模板:

{ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../dist/out-tsc", "module": "commonjs", "types": ["jest", "node"] }, "include": [ "jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts" ] }

当前仓库 e2e 项目的真实配置 e2e/jest/tsconfig.spec.json 还额外展示了"isolatedModules": true"rootDir": "."以及exclude: ["out-tsc"]的写法,include 数组覆盖了**/*.test.ts**/*.spec.ts、JS/JSX 变体与jest.config.ts,可直接作为参考。

导入后常见问题:

  • 缺少"types": ["jest", "node"]describe/it/expect全部无法识别,报Cannot find type definition file for 'jest'
  • 缺少"module": "commonjs"→ Jest 默认不支持 ESM(ts-jest 转译为 CJS),报Cannot use import statement outside a module
  • include数组缺少测试文件模式 → TypeScript 根本不会检查测试文件,类型错误被静默吞掉。

注意:types数组是显式列举而非自动发现,因此@types/jest必须安装且必须写入types;而 ts-jest 的 transform 会读取<rootDir>/tsconfig.spec.json,所以该文件路径与内容必须与 preset/项目配置对齐。

Jest 与 Vitest 共存

一个 workspace 可以同时存在两套测试体系:

  • Jest:Next.js 应用、老 React 库、Node 库;
  • Vitest:基于 Vite 的 React/Vue 应用与库。

@nx/jest/plugin@nx/vite/plugin(后者推断 Vitest target)互不冲突——它们检测不同的配置文件(jest.config.*vsvite.config.*),见 packages/jest/src/plugins/plugin.ts 中的配置 glob 与 Vite 插件的vite.config.*匹配。

唯一注意点:两者默认 target 名都是test。如果某个项目异常地同时拥有两套配置文件,需要重命名其中一个:

{ "plugin": "@nx/jest/plugin", "options": { "targetName": "jest-test" } }

另外,init 生成器在注册插件时默认会同时接受testjest:testjest-test三个 target 名(targetName: ['test', 'jest:test', 'jest-test']),为的就是与既有脚本或显式 target 平滑共存。

@testing-library/jest-dom:Jest 与 Vitest 的导入差异

从 Jest 迁移到 Vitest(或两者共存)的项目需要不同的导入路径。在test-setup.ts中:

Jest:

import '@testing-library/jest-dom';

Vitest:

import '@testing-library/jest-dom/vitest';

如果源仓库用的是 Jest 而目标 workspace 对该类项目使用 Vitest,记得更新导入路径,并把@testing-library/jest-dom加入 tsconfig 的types数组。

非 Nx 源仓库:package.json测试脚本重写问题

导入非 Nx 源仓库时,Nx 在 init 期间会重写package.json脚本,测试脚本尤其容易损坏:

  • "test": "jest""test": "nx test"(没有配置 executor 时形成循环调用);
  • "test": "vitest run""test": "nx test run"(损坏——run变成了参数)。

修复:删除所有被重写的测试脚本。@nx/jest/plugin@nx/vite/plugin会从配置文件推断 test target,package.json里不需要(也不应该有)重复的测试脚本。同理,如果源项目自带build/dev/start/lint脚本,Nx 插件会自动给推断 target 加前缀(如next:buildvite:buildeslint:lint)以避免冲突——要么接受前缀名(nx run app:next:build),要么在nx.json中重命名插件 target 名去掉前缀。

CI 原子化(Atomization)

@nx/jest/plugin支持按文件拆分测试任务,用于 CI 并行:

{ "plugin": "@nx/jest/plugin", "options": { "targetName": "test", "ciTargetName": "test-ci" } }

配置后,插件为每个测试文件生成形如test-ci--src/lib/foo.spec.ts的独立 target,并创建一个名为test-ci的聚合 target(executor 为nx:noop,通过dependsOn转发参数和选项到各原子任务)。每个原子任务执行jest <relativePath>,继承cacheinputsoutputs。这些任务通过targetGroups分组(ciGroupName),可直接由 Nx Cloud 分发执行。

从源码看(packages/jest/src/plugins/plugin.ts),原子化还有几个细节:

  • 通过 Jest 的SearchSource/getTestPaths或 Nx 自己的globWithWorkspaceContext枚举测试文件,并按路径排序保证 target 名稳定;
  • 尊重testPathIgnorePatterns,被忽略的文件不会生成原子任务;
  • 如果测试文件超出项目根目录会直接报错(防止错误配置静默放行);
  • 聚合 target 的 metadata 中包含nonAtomizedTarget: 'test',标记它对应的完整测试 target。

导入阶段用不到原子化,但它是导入后 CI 改造的直接收益点。

常见导入后问题清单

#错误信息根因修复
1Cannot find target 'test'@nx/jest/plugin未注册到nx.jsonnpx nx add @nx/jest或手动添加插件条目
2Cannot find module 'jest-preset'workspace 根目录缺少jest.preset.js手动创建(见上文 preset 一节)
3Cannot find type definition file for 'jest'缺少@types/jest,或tsconfig.spec.json没有"types": ["jest", "node"]安装依赖并修正 tsconfig
4Cannot use import statement outside a modulets-jest 未安装或未配置为 transform安装 ts-jest,检查jest.config.*的 transform 段
5快照路径不匹配导入后__snapshots__目录路径已固化--updateSnapshot跑一次测试重新生成

前两类问题与"Jest Preset Missing"基础修复的完整步骤同样记录在 .opencode/skills/nx-import/SKILL.md,本文不再重复。

推荐修复顺序

子目录导入(目标为 Nx 源)

  1. npx nx add @nx/jest—— 在nx.json注册插件(不会创建jest.preset.js);
  2. 手动创建jest.preset.js(内容见上文);
  3. 安装核心依赖:pnpm add -wD jest jest-environment-jsdom ts-jest @types/jest
  4. 按框架安装测试依赖:React 装@testing-library/react @testing-library/jest-dom,Vue 装@vue/test-utils
  5. 核对tsconfig.spec.json包含"types": ["jest", "node"]
  6. 运行nx run-many -t test验证。

整仓导入(非 Nx 源)

  1. 删除package.json中被重写的测试脚本;
  2. npx nx add @nx/jest—— 注册插件(不创建 preset);
  3. 手动创建jest.preset.js
  4. 安装依赖(同上);
  5. 核对/修复jest.config.*—— 确保preset路径指向根目录jest.preset.js
  6. 核对/修复tsconfig.spec.json—— 补上typesmoduleinclude
  7. 运行nx run-many -t test验证。

验证与进一步阅读

修复完成后,可以用 packages/jest/PLUGIN.md 中的命令做日常验证与单文件调试:

  • 推断模式(@nx/jest/plugin):nx test <project> -- --testPathPattern=<path/to/file.spec.ts>,按名称过滤用nx test <project> -- -t "pattern"
  • 显式 executor 模式(@nx/jest:jest):nx run <project>:test --testFile=<path/to/file.spec.ts>,按名称过滤用nx run <project>:test --testNamePattern="pattern"

插件本身的行为由 packages/jest/src/plugins/plugin.spec.ts 等测试保障;如果想深入理解 preset 默认值,直接阅读 packages/jest/preset/jest-preset.ts;生成器的完整流程(含@nx/jest:configuration如何同时创建 preset 与项目配置)见 packages/jest/src/generators/configuration/configuration.ts。

【免费下载链接】nxThe Monorepo Platform that amplifies both developers and AI agents. Nx optimizes your builds, scales your CI, and fixes failed PRs automatically. Ship in half the time.项目地址: https://gitcode.com/GitHub_Trending/nx/nx

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询