拓冰建站拓冰建站
首页 / 资讯中心 / 正文

使用 Jest 测试 React 应用:从环境搭建、快照测试到 DOM 交互的完整实战指南

使用 Jest 测试 React 应用从环境搭建、快照测试到 DOM 交互的完整实战指南【免费下载链接】jestDelightful JavaScript Testing.项目地址: https://gitcode.com/gh_mirrors/je/jestJest 本身就是 FacebookMeta在测试 React 应用的过程中诞生并持续打磨的测试框架本指南围绕 Jest 29 中《Testing React Apps》的完整脉络讲解如何在 React 项目中配置 Jest含 Create React App 与手动搭建两条路径、用react-test-renderer编写快照测试、处理 Enzyme 与 React 16 的兼容警告以及用testing-library/react做 DOM 交互断言最后深入自定义 transformer 的编写。读完本文你将获得一套可直接复制、可运行、有仓库源码佐证的 React 测试方案。Setup搭建 React 项目的 Jest 测试环境使用 Create React App推荐新手如果你刚开始接触 React官方推荐直接使用 Create React App。它开箱即用且自带 Jest——创建项目后无需任何额外 Jest 配置即可运行测试。你唯一需要补充安装的是react-test-renderer用于渲染组件并生成快照npm install --save-dev react-test-renderer不依赖 Create React App 手动搭建如果你在已有应用或自定义脚手架中接入 Jest则需要安装一组包让 Babel 转换与测试环境协同工作。核心思路是用babel-jest配合 React 的 Babel preset在测试环境中把 JSX / 新版 JavaScript 语法转换为 Jest 可执行的代码详见 使用 Babelnpm install --save-dev jest babel-jest babel/preset-env babel/preset-react react-test-renderer安装完成后package.json大致如下current-version表示对应包的当前实际版本请以你安装到的版本为准。请务必补上scripts.test与 jest 相关配置项{ dependencies: { react: current-version, react-dom: current-version }, devDependencies: { babel/preset-env: current-version, babel/preset-react: current-version, babel-jest: current-version, jest: current-version, react-test-renderer: current-version }, scripts: { test: jest } }并在项目根目录创建 Babel 配置文件module.exports { presets: [ babel/preset-env, [babel/preset-react, {runtime: automatic}], ], };这里runtime: automatic表示使用 React 17 的 JSX 转换方式无需在文件顶部显式import React。做完以上两步就可以开始写测试了。Snapshot Testing用 react-test-renderer 捕获组件渲染结果编写一个 Link 组件的快照测试以渲染超链接的Link组件为例完整示例见 examples/snapshot/Link.js它根据鼠标悬停状态切换classNameimport {useState} from react; const STATUS { HOVERED: hovered, NORMAL: normal, }; export default function Link({page, children}) { const [status, setStatus] useState(STATUS.NORMAL); const onMouseEnter () { setStatus(STATUS.HOVERED); }; const onMouseLeave () { setStatus(STATUS.NORMAL); }; return ( a className{status} href{page || #} onMouseEnter{onMouseEnter} onMouseLeave{onMouseLeave} {children} /a ); }说明示例使用函数组件但类组件可以用完全相同的方式测试参见 React 官方文档 Function and Class Components。需要提醒的是对于类组件Jest 期望测试的是props对外行为而非直接调用内部方法。接下来用 React 的react-test-renderer渲染组件、通过toMatchSnapshot()捕获渲染输出并借助renderer.act()手动触发回调验证状态切换前后的三种渲染形态import renderer from react-test-renderer; import Link from ../Link; it(changes the class when hovered, () { const component renderer.create( Link pagehttp://www.facebook.comFacebook/Link, ); let tree component.toJSON(); expect(tree).toMatchSnapshot(); // manually trigger the callback renderer.act(() { tree.props.onMouseEnter(); }); // re-rendering tree component.toJSON(); expect(tree).toMatchSnapshot(); // manually trigger the callback renderer.act(() { tree.props.onMouseLeave(); }); // re-rendering tree component.toJSON(); expect(tree).toMatchSnapshot(); });生成的快照文件长什么样运行yarn test或jest后会在__tests__/__snapshots__/目录下生成与测试文件同名的.snap文件仓库中的真实产物见 examples/snapshot/tests/snapshots/link.test.js.snap内容形如exports[changes the class when hovered 1] a classNamenormal hrefhttp://www.facebook.com onMouseEnter{[Function]} onMouseLeave{[Function]} Facebook /a ; exports[changes the class when hovered 2] a classNamehovered hrefhttp://www.facebook.com onMouseEnter{[Function]} onMouseLeave{[Function]} Facebook /a ; exports[changes the class when hovered 3] a classNamenormal hrefhttp://www.facebook.com onMouseEnter{[Function]} onMouseLeave{[Function]} Facebook /a ;每个exports[...]块对应测试中一次toMatchSnapshot()调用初始状态classNamenormal、悬停后classNamehovered、离开后又回到normal完整记录了组件状态机随交互变化的渲染结果。快照的更新与提交快照文件应当与代码变更一起提交到版本库。下次运行测试时渲染输出会与已保存的快照逐字对比一旦不匹配测试即失败。此时需要人工判断这是预期的变更例如 UI 调整还是意外回归。若确认是预期变更运行jest -u即--updateSnapshot覆盖既有快照。该示例的完整可运行代码位于仓库的 examples/snapshot 目录其中package.json声明了jest配置testEnvironment: jsdom与下面 DOM 测试的场景相互配合。深入快照断言与序列化背后的机制快照测试并非魔术。toMatchSnapshot()在 packages/jest-snapshot 包中实现首次执行时把序列化后的值写入.snap文件文件头会标注// Jest Snapshot v1之后每次执行都会反序列化旧快照并与新值比对。在 React 场景中react-test-renderer的toJSON()负责把组件树折叠为纯 JSON 结构标签名、props、子节点因此快照文件里能看到完整的className、href与函数引用占位符。理解这一点你就知道为什么“快照对比的是渲染结果而非组件内部状态”。Snapshot Testing with Mocks, Enzyme and React 16在 Enzyme React 16 的组合下做快照测试有一个知名坑如果按下面这种“字符串短路”的方式 mock 组件jest.mock(../SomeDirectory/SomeComponent, () SomeComponent);控制台会出现警告Warning: SomeComponent / is using uppercase HTML. Always use lowercase HTML tags in React. # Or: Warning: The tag SomeComponent is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.原因是 React 16 对元素类型有严格的校验逻辑而上述 mock 返回的字符串无法通过该校验。有四种解决方案按推荐程度排序渲染为纯文本简单直接但快照中看不到传给 mock 组件的 propsjest.mock(./SomeComponent, () () SomeComponent);渲染为自定义元素DOM “自定义元素”custom elements不参与任何类型校验、不会触发警告。其规范要求小写且名称含连字符jest.mock(./Widget, () () mock-widget /);改用react-test-renderer测试渲染器不关心元素类型可以愉快地接受如SomeComponent这种大写标签。实践中可以用测试渲染器做快照断言、用 Enzyme 单独验证组件行为。整体禁用警告在 jest setup 文件中配置jest.mock(fbjs/lib/warning, () require(fbjs/lib/emptyFunction));这通常不应成为首选因为会连带丢失真正有价值的警告。但在某些场景下是合理的例如测试 react-native 组件时把 react-native 标签渲染进了 DOM大量警告本身无意义。另一种思路是替换console.warn仅屏蔽特定警告。DOM Testing断言并操纵已渲染的组件如果需要在真实 DOM 环境中做断言并操纵渲染结果可以使用 testing-library/react、Enzyme 或 React 官方的 TestUtils。下面以testing-library/react为例。安装npm install --save-dev testing-library/react注意testing-library/react9.0.0及以上版本会自动在afterEach中执行 cleanup卸载组件、清理 DOM因此下文示例中的afterEach(cleanup)在实际高版本中可以省略——仓库示例 examples/react-testing-library/tests/CheckboxWithLabel-test.js 就没有显式 cleanup。先实现一个在两个标签文案间切换的复选框组件import {useState} from react; export default function CheckboxWithLabel({labelOn, labelOff}) { const [isChecked, setIsChecked] useState(false); const onChange () { setIsChecked(!isChecked); }; return ( label input typecheckbox checked{isChecked} onChange{onChange} / {isChecked ? labelOn : labelOff} /label ); }对应测试完整示例见 examples/react-testing-library其package.json使用testEnvironment: jsdomimport {cleanup, fireEvent, render} from testing-library/react; import CheckboxWithLabel from ../CheckboxWithLabel; // Note: running cleanup afterEach is done automatically for you in testing-library/react9.0.0 or higher // unmount and cleanup DOM after the test is finished. afterEach(cleanup); it(CheckboxWithLabel changes the text after click, () { const {queryByLabelText, getByLabelText} render( CheckboxWithLabel labelOnOn labelOffOff /, ); expect(queryByLabelText(/off/i)).toBeTruthy(); fireEvent.click(getByLabelText(/off/i)); expect(queryByLabelText(/on/i)).toBeTruthy(); });这个测试完整演示了 DOM 测试的典型链路render()把组件挂载进 jsdom 模拟的 DOM 中返回queryByLabelText、getByLabelText等查询方法断言初始态queryByLabelText(/off/i)用正则按 label 文本查询元素验证初始渲染的是 OfffireEvent.click()模拟用户点击由于input包裹在label中点击 label 会命中复选框断言交互结果点击后状态翻转查询on文本成功证明onChange事件处理生效。仓库中 examples/snapshot/tests/link.test.js 还展示了更贴近真实用户的写法用testing-library/user-event的userEvent.hover()/userEvent.unhover()触发悬停配合screen.findByLabelText()异步断言 class 变化可以与本指南的react-test-renderer方案对照学习。Custom Transformers定制代码转换逻辑如果你的项目需要更高级的转换能力可以不使用babel-jest而自己编写 transformer。下面的例子基于babel/core实现对每个源文件调用 Babel 的transform并混入babel-preset-jest该 preset 负责注入 Jest 所需的 Babel 插件如 hoistjest.mock调用use strict; const {transform} require(babel/core); const jestPreset require(babel-preset-jest); module.exports { process(src, filename) { const result transform(src, { filename, presets: [jestPreset], }); return result || src; }, };别忘了为这个示例安装babel/core和babel-preset-jest两个包。然后在 Jest 配置中注册该 transformer将.js文件指向它transform: {\\.js$: path/to/custom-transformer.js}基于 babel-jest 组合自定义 transformer如果你需要 Babel 支持、又想要自定义配置更优雅的做法是直接基于babel-jest的createTransformer组合它返回一个符合 Jest transformer 接口的对象你只需传入自己的 Babel 选项const babelJest require(babel-jest); module.exports babelJest.createTransformer({ presets: [my-custom-preset], });从源码看createTransformer在 packages/babel-jest/src/index.ts 中实现有几个值得了解的细节自动注入 jest presetcreateTransformer会把babel-preset-jest追加到presets末尾除非显式传入excludeJestPreset: true同时设置caller: {name: babel-jest, ...}让 Babel 感知调用方能力缓存键cache keygetCacheKey基于源码文本、Babel 配置、相对路径、NODE_ENV、BABEL_ENV与 Node 版本等信息计算 SHA-1 哈希取前 32 位保证配置或环境变化时缓存自动失效覆盖率插桩当transformOptions.instrument为真即开启 coverage时会自动注入babel-plugin-istanbul实现源码级覆盖率无需手工配置source map转换输出默认携带sourceMaps: both便于堆栈溯源。想深入 transformer 的完整接口process/getCacheKey/canInstrument等可参考 CodeTransformation.md 的专项文档。小结围绕 Jest 29 的 React 测试指南本文覆盖了从零到实战的完整路径搭建阶段可选择 Create React App 零配置接入或手动配置babel-jest React preset快照测试用react-test-renderer捕获渲染输出并用jest -u管理快照更新DOM 测试推荐testing-library/react以用户视角断言交互Enzyme React 16场景下则需按四种方案规避 mock 警告定制化需求可通过自定义 transformer 或组合babel-jest.createTransformer实现。上述所有示例均可在仓库 examples/snapshot 与 examples/react-testing-library 中找到可运行源码作为你搭建 React 测试体系时的直接参照。【免费下载链接】jestDelightful JavaScript Testing.项目地址: https://gitcode.com/gh_mirrors/je/jest创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门