测试 LWC — Jest 单元测试完全指南

全面掌握 LWC Jest 测试。...

📅 2026/7/19 ✍️ ponybai 🏷️ lwc, salesforce

测试 Lightning Web Components

测试 LWC

使用 Jest 编写 LWC 单元测试——命令行或 IDE 中运行、不连接浏览器或 org(极快)、watch mode(编码时即时反馈)、仅支持 LWC(不支持 Aura)。测试内容:隔离测试组件、测试 @api 公共 API、测试基本用户交互(click)、验证 DOM 输出、验证事件触发时机。测试文件保存在本地——不部署到 Salesforce

安装与运行 Jest 测试

安装与运行

前提:Node.js(推荐 LTS)+ npm。仅支持 Salesforce DX 项目。

最简单安装——Salesforce CLI:sf force lightning lwc test setup(项目根目录运行)——自动创建配置文件+安装 sfdx-lwc-jest

手动安装:npm install @salesforce/sfdx-lwc-jest --save-dev。package.json scripts:

"test:unit": "sfdx-lwc-jest",
"test:unit:watch": "sfdx-lwc-jest --watch",
"test:unit:debug": "sfdx-lwc-jest --debug",
"test:unit:coverage": "sfdx-lwc-jest --coverage"

运行方式:VS Code(Salesforce Extension Pack→LWC Tests 侧边栏→运行/调试单个或所有测试)。CLI:npm run test:unit(所有测试)/npm run test:unit:watch(持续监控——保存即运行相关测试)。速度慢时配置 moduleNameMapper 加速依赖解析(参考 lwc-recipes jest.config.js)。

调试 Jest 测试 —— 三种方法

调试 Jest 测试

方法 1——Salesforce Extension Pack(最简单):LWC Tests 侧边栏→在测试代码中设断点(点击行号)→点击 Debug Test→Run view 中查看表达式/调试工具栏/Debug Console。使用 Continue 逐步通过断点。

方法 2——Chrome DevTools:npm run test:unit:debug(运行 node --inspect-brk)→浏览器打开 chrome://inspect→Remote Target 中点击 inspect→Sources 面板中注意 VS Code 断点无效——用 debugger 语句代替→Resume 按钮继续执行→在 Sources 面板中设断点。

方法 3——VS Code 高级配置(最灵活):创建 launch.json(Node.js 模板)→添加 jest.config.js→使用 debugger 语句(不支持断点——行号不匹配编译后文件)→Run view 选择 Debug Jest Tests→启动。支持自定义调试器和场景。

配置加速测试:如果测试执行慢且有跨文件夹依赖→用 moduleNameMapper 加速模块解析。

编写 Jest 测试 —— 完整结构

编写 Jest 测试

测试文件结构:组件 __tests__ 文件夹→componentName.test.js(推荐 .test.js 后缀)。.forceignore 加 **/__tests__/** 防止部署到 Salesforce。CLI:sf force lightning lwc test create -f path/to/component.js 自动创建。

测试八要素:

  1. 导入:import { createElement } from "lwc" + import Component from "c/component"
  2. describe:测试套件——推荐顶层 describe 匹配组件名(如 describe('c-hello', ...)
  3. afterEach 清理:每个测试文件共享同一个 jsdom 实例——必须清空 DOM(while(document.body.firstChild) removeChild)防止测试互相影响
  4. it/test:单个测试——描述预期行为(如 it('displays greeting', ...)
  5. createElement:createElement("c-hello", { is: Hello })——仅测试中可用
  6. appendChild:插入 DOM→触发 connectedCallback + renderedCallback
  7. 查询:element.shadowRoot.querySelector("div")——shadowRoot 是测试专用 API(等价于 this.template
  8. 断言:Jest matchers——expect(text).toBe("Hello, World!")

异步 DOM 更新:属性在 appendChild 后设置→DOM 异步更新→用 return Promise.resolve().then(() => {...}) 等待。属性在 appendChild 前设置→同步渲染→无需 Promise。

导航服务测试:创建 navigation mock(NavigationMixin/CurrentPageReference/mockNavigate/mockGenerate)→jest.config.js 中配置 moduleNameMappergetNavigateCalledWith() 验证导航参数。

测试 Wire Service 组件

测试 Wire Service

输入不应依赖外部代码/数据——完全控制输入。使用 sfdx-lwc-jest 工具提供三种 adapter mock:通用 adapter(emit() 按需发数据)、LDS adapter(模拟 Lightning Data Service 行为含数据属性)、Apex adapter(模拟 Apex 方法含错误状态)。

三种 adapter mock 区别:

  • Generic adapter:createTestWireAdapter(jest.fn())——按需 emit 数据,无数据属性额外信息。适合简单场景
  • LDS adapter:直接导入真实 wire adapter(如 getRecord from "lightning/uiRecordApi")——mock 自动创建,包含数据属性信息,模拟 LDS 缓存行为
  • Apex adapter:模拟 Apex 方法调用——包含错误状态和 Promise 行为。用 jest.fn() 创建 mock 函数→mockResolvedValue()/mockRejectedValue() 控制返回值

标准流程:① 在 __tests__/data/ 创建 JSON 文件(命名匹配 wire adapter,如 getRecord.json)——推荐用 REST 客户端抓取 /ui-api/records/{recordId} 响应快照(比手写 JSON 更准确) ② 测试中导入 mock JSON + wire adapter ③ getRecord.emit(mockGetRecord) 发射数据 ④ return Promise.resolve().then(() => { /* 验证 DOM */ }) 等待异步渲染后验证。组件仅在连接到 DOM 后接收 wire 数据更新。

命令式 Apex 测试:jest.config.js 配 moduleNameMapper: { '^@salesforce/apex$': '<rootDir>/force-app/test/jest-mocks/apex' }→mock 文件中 export const createContact = jest.fn()→测试中 createContact.mockResolvedValue({ id: '003...' }) 设返回值→调用组件方法→await Promise.resolve() 等待异步→验证 expect(createContact).toHaveBeenCalledWith({ lastName: 'Smith' }) 检查参数+ expect(element.contactId).toBe(mockId) 检查状态。Update: mockResolvedValue({ success: true })。Delete: mockResolvedValue()(无返回值)。每个 afterEach 中必须 jest.clearAllMocks() 重置 mock 状态。

注意:Spring '21 之前需手动注册 wire adapter——现在不再需要。直接导入即可。

Mock 模式与 DOM 检查考量

Mock 模式与 DOM 检查

属性变更测试:appendChild→Object.assign(element, attributes) 设属性→Promise.resolve().then() 等待异步渲染→断言。

基础组件 Mock:sfdx-lwc-jest 提供 lightning-stubs 目录中的 mock 组件——匹配实际组件 API 但无完整功能。Mock 不触发事件但可手动 dispatchEvent。注意事项:不依赖 slot 渲染顺序、部分属性不反映为 DOM 属性(如 iconPosition)、不触发事件但可 dispatchEvent。

事件处理器 Mock:const handler = jest.fn()element.addEventListener('eventName', handler)→断言 expect(handler).toHaveBeenCalled() + 验证 detail 属性值。

@salesforce 作用域导入 Mock:Label 导入→jest-transformer 自动转为变量声明(默认值为标签路径字符串)。自定义值:jest.mock("@salesforce/label/c.specialLabel", () => ({ default: "value set in test" }), { virtual: true })。Module Name Mapper 完整示例(lwc-recipes 常用映射):'^lightning/navigation$'→自定义 navigation mock、'^lightning/platformShowToastEvent$'→自定义 toast mock、'^lightning/uiRecordApi$'→自定义 UI API mock、'^lightning/messageService$'/'^lightning/actions$'/'^lightning/modal$'/'^lightning/refresh$'/'^lightning/logger$'——均可自定义 mock。覆盖默认配置时用 { ...jestConfig, setupFilesAfterEnv } 合并而非替换。

DOM 检查脆弱性:Salesforce 不保证向后兼容的 HTML/CSS/DOM——UI 测试(Selenium WebDriver)需持续维护。Jest 中 element.shadowRoot 是测试专用 API(可穿透 Shadow 边界)。Selenium 中用 JS 查询 $0.shadowRoot(合成 shadow polyfill 下返回 #document-fragment)。推荐 Jest 用于单元测试 + Selenium 仅用于端到端测试。

感谢阅读本指南。如需继续学习,请参阅下一章:使用 DX MCP 工具。