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

enzyme 与主流测试工具链集成指南:Jest、Mocha、Karma、JSDOM 与 React Native 全场景配置详解

测试前端【免费下载链接】enzymeJavaScript Testing utilities for React项目地址https://gitcode.com/gh_mirrors/en/enzyme点击查看免费下载enzyme 是专为 React 设计的 JavaScript 测试工具库其核心价值在于提供shallow、mount、render三种渲染方式与丰富的查询断言 API让组件测试覆盖从单层渲染到完整 DOM 挂载的各个粒度。本文以 docs/guides.md 的九篇官方集成指南为骨架系统讲解 enzyme 如何与 Jest、Mocha、Karma、JSDOM、Webpack、Browserify、React Native、Lab、Tape/AVA 等主流测试运行器与打包工具协同工作读完你将掌握每种场景下的环境搭建、配置要点、Adapter 初始化与典型测试写法。在进入各工具的具体配置之前有必要先厘清一个贯穿所有指南的前提enzyme 从 3.0 起必须显式配置一个与当前 React 版本匹配的 Adapter。仓库 packages/enzyme/src/index.js 将configure直接导出为 configuration.js 中的merge函数而 validateAdapter.js 会严格校验传入对象——未传 Adapter、传入构造函数而非实例、或实例未继承EnzymeAdapter基类时都会抛出带明确修复提示的Enzyme Internal Error。因此无论使用下方哪种测试框架第一步都是执行Enzyme.configure({ adapter: new Adapter() })。一、使用 Jest 测试 enzyme 组件Jest 是 Facebook 出品的零配置测试框架enzyme 官方在 docs/guides/jest.md 中按 Jest 版本差异给出了两套配置路径。1.1 配置 setup 文件Jest 15 及以上要运行配置 enzyme 与 Adapter 的 setup 文件安装说明见 docs/installation/README.md需要在 Jest 配置文件里设置setupFilesAfterEnv旧版名为setupTestFrameworkScriptFile其值固定为字符串rootDir拼接 setup 文件的路径{ jest: { setupFilesAfterEnv: [rootDirsrc/setupTests.js] } }在src/setupTests.js中完成 Adapter 注册import Enzyme from enzyme; import Adapter from enzyme-adapter-react-16; Enzyme.configure({ adapter: new Adapter() });1.2 Jest 15 及以上版本零额外配置从 Jest 15 开始Jest默认不再自动 mock 模块因此使用 enzyme 不再需要任何特殊配置。只需按 Jest 官方文档安装 Jest 及其 Babel 集成、安装 enzyme然后在测试文件顶部直接导入 React、enzyme 函数和被测模块即可import React from react; import { shallow, mount, render } from enzyme; import Foo from ../Foo;不需要引入 Jest 自己的渲染器除非你只想用它做 Jest 快照测试。1.3 Jest 15 之前的版本关闭 automocking如果使用 Jest 0.9 – 14.0 且启用了 Jest 的 automocking 特性需要在package.json中把 react 和 enzyme 标记为不 mock{ jest: { unmockedModulePathPatterns: [ node_modules/react/, node_modules/enzyme/ ] } }若结合 npm3 使用更早版本的 Jest可能还需要解除对更多模块的 mock。二、使用 Mocha 测试 enzyme 组件enzyme 最初就是为 Mocha 设计的因此接入成本极低官方指南 docs/guides/mocha.md 只要求两步安装依赖、直接编写测试。npm i --save-dev enzyme一个结合 Chai 断言与 Sinon spy 的完整示例import React from react; import { expect } from chai; import { mount } from enzyme; import { spy } from sinon; import Foo from ./src/Foo; spy(Foo.prototype, componentDidMount); describe(Foo /, () { it(calls componentDidMount, () { const wrapper mount(Foo /); expect(Foo.prototype.componentDidMount).to.have.property(callCount, 1); }); });注意mount需要真实 DOM。在 Node 环境运行 Mocha 前必须配合 docs/guides/jsdom.md 的 JSDOM setup 文件通过mocha --require setup.js预加载全局 DOM。三、使用 JSDOM 模拟浏览器 DOM3.1 为什么需要 JSDOMJSDOM 是一个基于 JavaScript 实现的 headless 浏览器可以创建接近真实的测试环境。enzyme 的mountAPI 依赖 DOM因此在非浏览器环境如 Node下必须借助 JSDOM 才能使用mount做全量挂载。官方指南 docs/guides/jsdom.md 强调为了获得最佳体验应在首次requireReact 之前就把 document 加载到全局作用域该 setup 脚本必须早于 React 代码执行。3.2 新版 JSDOM APIv10 及以上的 setup 文件/* setup.js */ const { JSDOM } require(jsdom); const jsdom new JSDOM(!doctype htmlhtmlbody/body/html); const { window } jsdom; function copyProps(src, target) { Object.defineProperties(target, { ...Object.getOwnPropertyDescriptors(src), ...Object.getOwnPropertyDescriptors(target), }); } global.window window; global.document window.document; global.navigator { userAgent: node.js, }; global.requestAnimationFrame function (callback) { return setTimeout(callback, 0); }; global.cancelAnimationFrame function (id) { clearTimeout(id); }; copyProps(window, global);关键点requestAnimationFrame/cancelAnimationFrame用setTimeout/clearTimeout模拟是为了兼容 React 内部对动画帧调度的依赖最后通过copyProps把 window 上所有属性定义到 global确保组件代码在测试中拿到的window、document与浏览器行为一致。3.3 旧版 JSDOM APIv10 之前的 setup 文件/* setup.js */ const { jsdom } require(jsdom); global.document jsdom(); global.window document.defaultView; global.navigator { userAgent: node.js, }; function copyProps(src, target) { const props Object.getOwnPropertyNames(src) .filter((prop) typeof target[prop] undefined) .reduce((result, prop) ({ ...result, [prop]: Object.getOwnPropertyDescriptor(src, prop), }), {}); Object.defineProperties(target, props); } copyProps(document.defaultView, global);两版差异主要在于 JSDOM 构造 API 的形态new JSDOM(html)vsjsdom(html)与属性拷贝方式新版用getOwnPropertyDescriptors完整复制旧版仅复制目标上不存在的属性。仓库 packages/enzyme/withDom.js 给出了 enzyme 内部兜底实现——当global.document缺失时自动尝试加载 jsdom 并注入全局若未安装 jsdom 会打印npm install jsdom --save-dev的修复提示可作为手工 setup 的参考。3.4describeWithDOM已成过去式旧版 enzyme 曾提供公开的describeWithDOMAPI会在每个测试前把新 JSDOM document 重新载入全局命名空间以保证测试确定性。但 React 源码假设 require 时拿到的global.document就是唯一需要关心的 document这种反复 reload 反而会引发更多问题因此该方式不再被推荐。你需要自行保证使用全局 DOM API 的测试不产生泄漏的副作用。3.5 JSDOM Mocha 的命令行接入setup 文件必须在测试套件运行前执行Mocha 可用--require选项从命令行加载mocha --require setup.js --recursive path/to/test/dir3.6 Node.js 兼容性与版本切换JSDOM 要求 Node 4 及以上。若想用它配合mount请确保机器上装有 Node 4 或 iojs若受困于更老的 Node 版本可改用基于浏览器的测试运行器 Karma。需要切换 Node 版本时可用nvm先用 curl 脚本从 https://nvm.sh 安装然后执行nvm install 4安装指定版本再用nvm use 0.12或nvm use 4在两个环境间切换。四、使用 Karma 在真实浏览器中运行 enzymeKarma 是支持在多种浏览器环境运行测试的流行运行器。官方指南 docs/guides/karma.md 给出了基础配置与 karma-webpack 替代方案。4.1 基础配置创建 enzyme setup 文件创建一个 Enzyme setup 文件它负责配置与当前 React 匹配的 Adapter并集中初始化所有测试共用的全局辅助为了避免每个测试重复导入 enzyme 与该文件可以从该文件重新导出 enzyme 的全部导出/* test/enzyme.js */ import Enzyme from enzyme; import Adapter from enzyme-adapter-react-16; import jasmineEnzyme from jasmine-enzyme; // Configure Enzyme for the appropriate React adapter Enzyme.configure({ adapter: new Adapter() }); // Initialize global helpers beforeEach(() { jasmineEnzyme(); }); // Re-export all enzyme exports export * from enzyme;4.2 从 setup 文件导入任何需要使用 enzyme 的地方改为从 setup 文件导入即可用法与直接import from enzyme完全一致/* some_test.js */ // Import anything you would normally import from enzyme from the Enzyme setup file import { shallow } from ./test/enzyme; // ...4.3 备选karma-webpack 的 alternative 用法如果采用 karma-webpack 的 alternative 配置方式可以在测试入口文件里完成 enzyme 配置然后各个测试直接导入 enzyme/* test/index_test.js */ import ./enzyme; const testsContext require.context(., true, /_test$/); testsContext.keys().forEach(testsContext);/* some_test.js */ // If Enzyme is configured in the test entry file, Enzyme can be imported directly import { shallow } from enzyme; // ...五、使用 Webpack 与 Browserify 打包测试代码如果测试运行在基于浏览器的环境中通常需要用打包器预处理 React 代码。官方指南 docs/guides/webpack.md 与 docs/guides/browserify.md 的说明完全一致enzyme 3.0 之前为了兼容多个 React 版本而使用的条件require曾给打包带来麻烦enzyme 3.0 之后这不再成为问题。如果仍然遇到打包问题请到仓库提交 issue 或 PR 补充对应打包器的配置说明。也就是说enzyme 3.0 起无需为 Webpack/Browserify 添加任何特殊配置直接按常规方式把测试与组件代码打入 bundle 即可。六、在 React Native 中测试组件自 React Native v0.18 起RN 将 React 作为普通依赖而非 fork 版本因此 enzyme 的shallow可以直接用于 RN 组件。但 RN 依赖大量难以在无宿主设备环境下模拟的环境依赖这让测试套件很难跑在 Travis 等 CI 服务器上。官方指南 docs/guides/react-native.md 给出的完整方案是配置 Adapter 加载模拟 DOM。6.1 配置 Adapter虽然专用的 React Native adapter 仍在讨论中但可以先用标准 adapter例如enzyme-adapter-react-16import Adapter from enzyme-adapter-react-16; Enzyme.configure({ adapter: new Adapter() });6.2 用 JSDOM 加载模拟 DOM在专用 adapter 出现之前要使用mount就必须加载模拟 DOM。虽然有人用react-native-mock-renderer取得成功但推荐方案是 JSDOM做法即本文第三节的 setup 脚本。JSDOM 能支持 enzyme 的全部预期行为Jest 快照测试虽也可用但不被鼓励且只通过wrapper.debug()支持。6.3 用testID代替className查找节点RN 组件没有className但提供了testIDprop可以像标准 React 中的className一样作为选择器View key{key} style{styles.todo} testIDtodo-item Text testIDtodo-title style{styles.title}{todo.title}/Text /Viewexpect(wrapper.findWhere((node) node.prop(testID) todo-item)).toExist();6.4 Jest JSDOM 替换的默认示例配置推荐用 setup 脚本完成所需配置例如通过 Jest 的setupFilesAfterEnv。在项目根目录创建或更新jest.config.js// jest.config.js module.exports { // Load setup-tests.js before test execution setupFilesAfterEnv: rootDirsetup-tests.js, // ... };再创建或更新 setup 脚本setup-tests.js其中既加载 JSDOM 全局 DOM又配置 enzyme// setup-tests.js import react-native; import jest-enzyme; import Adapter from enzyme-adapter-react-16; import Enzyme from enzyme; /** * Set up DOM in node.js environment for Enzyme to mount to */ const { JSDOM } require(jsdom); const jsdom new JSDOM(!doctype htmlhtmlbody/body/html); const { window } jsdom; function copyProps(src, target) { Object.defineProperties(target, { ...Object.getOwnPropertyDescriptors(src), ...Object.getOwnPropertyDescriptors(target), }); } global.window window; global.document window.document; global.navigator { userAgent: node.js, }; copyProps(window, global); /** * Set up Enzyme to mount to DOM, simulate events, * and inspect the DOM in tests. */ Enzyme.configure({ adapter: new Adapter() });6.5 与其他测试库搭配按需加载 JSDOM如果不想全局加载 JSDOM可以在测试文件层面用jest-environment jsdom按需切换import react-native; import jest-enzyme; import Adapter from enzyme-adapter-react-16; import Enzyme from enzyme; /** * Set up Enzyme to mount to DOM, simulate events, * and inspect the DOM in tests. */ Enzyme.configure({ adapter: new Adapter() });创建以enzyme.test命名的独立测试文件例如component.enzyme.test.js/** * jest-environment jsdom */ import React from react; import { mount } from enzyme; import { Text } from ../../../component/text; describe(Component tested with enzyme, () { test(App mount with enzyme, () { const wrapper mount(Text /); // other tests operations }); });最关键的一点确保测试在jestEnvironment为jsdom的环境下运行——在文件顶部加/* jest-environment jsdom */注释是方式之一。6.6 完整实战示例TodoList 的 enzyme 测试对于原生组件可能需要额外 mock若要对 RN 组件做快照测试还需注意 mock 掉 React Navigation 的KeyGenerator——它生成随机 React key 会导致快照永远失败import React from react; import renderer from react-test-renderer; import { mount, ReactWrapper } from enzyme; import { Provider } from mobx-react; import { Text } from native-base; import { TodoItem } from ./todo-item; import { TodoList } from ./todo-list; import { todoStore } from ../../stores/todo-store; // React Navigation generates random React keys, which makes // snapshot testing fail. Mock the randomness to keep from failing. jest.mock(react-navigation/src/routers/KeyGenerator, () ({ generateKey: jest.fn(() 123), })); describe(todo-list, () { describe(enzyme tests, () { it(can add a Todo with Enzyme, () { const wrapper mount( Provider keyLength{0} todoStore{todoStore} TodoList / /Provider, ); const newTodoText I need to do something...; const newTodoTextInput wrapper.find(Input).first(); const addTodoButton wrapper .find(Button) .findWhere((w) w.text() Add Todo) .first(); newTodoTextInput.props().onChangeText(newTodoText); // Enzyme usually allows wrapper.simulate() alternatively, but this doesnt support press events. addTodoButton.props().onPress(); // Make sure to call update if external events (e.g. Mobx state changes) // result in updating the component props. wrapper.update(); // You can either check for a testID prop, similar to className in React: expect( wrapper.findWhere((node) node.prop(testID) todo-item), ).toExist(); // Or even just find a component itself, if you broke the JSX out into its own component: expect(wrapper.find(TodoItem)).toExist(); // You can even do snapshot testing, // if you pull in enzyme-to-json and configure // it in snapshotSerializers in package.json expect(wrapper.find(TodoList)).toMatchSnapshot(); }); }); });该示例展示了 RN 测试的几个关键技巧用wrapper.find(Input)定位输入框并通过onChangeText触发文本变更由于simulate不支持 press 事件按钮点击需直接调用onPressprop外部状态如 Mobx引起 props 变化后要调用wrapper.update()同步断言既可用testID也可直接查找组件本身。七、使用 Lab 与 Code 测试 enzyme 组件Lab 是 hapi.js 生态中的 Node 测试工具其早期代码大量借鉴 MochaCode 则是 Lab 配套的断言库Chai 的直接重写。官方指南 docs/guides/lab.md 给出了完整示例import { shallow, mount, render } from enzyme; import React from react; const Code require(code); const Lab require(lab); const lab Lab.script(); export { lab }; lab.suite(A suite, () { lab.test(calls componentDidMount, (done) { const wrapper mount(Foo /); Code.expect(Foo.prototype.componentDidMount.callCount).to.equal(1); done(); }); });要点通过Lab.script()创建脚本并导出lab用lab.suite/lab.test组织用例异步断言完成后调用done()收尾。八、使用 Tape 与 AVA 测试 enzyme 组件enzyme 对 Tape 和 AVA 的支持同样开箱即用。官方指南 docs/guides/tape-ava.md 建议一次性安装 enzyme 与对应 adapternpm i --save-dev enzyme enzyme-adapter-react-168.1 Tapeimport test from tape; import React from react; import { shallow, mount, configure } from enzyme; import Adapter from enzyme-adapter-react-16; import Foo from ../path/to/foo; configure({ adapter: new Adapter() }); test(shallow, (t) { const wrapper shallow(Foo /); t.equal(wrapper.contains(spanFoo/span), true); }); test(mount, (t) { const wrapper mount(Foo /); const fooInner wrapper.find(.foo-inner); t.equal(fooInner.is(.foo-inner), true); });8.2 AVAimport test from ava; import React from react; import { shallow, mount, configure } from enzyme; import Adapter from enzyme-adapter-react-16; import Foo from ../path/to/foo; configure({ adapter: new Adapter() }); test(shallow, (t) { const wrapper shallow(Foo /); t.is(wrapper.contains(spanFoo/span), true); }); test(mount, (t) { const wrapper mount(Foo /); const fooInner wrapper.find(.foo-inner); t.is(fooInner.is(.foo-inner), true); });两种框架的写法几乎一致仅断言 API 不同Tape 用t.equalAVA 用t.is测试命名上 Tape 是test(..., (t) ...)AVA 是test(..., (t) ...)且均为异步安全。二者都要求在任何测试前先执行configure({ adapter: new Adapter() })。九、集成要点与源码佐证9.1 Adapter 是唯一的必修课纵观全部九篇指南无论选择哪种测试框架唯一不可省略的步骤都是Enzyme.configure({ adapter: new Adapter() })。从源码看packages/enzyme/src/configuration.js 中merge在设置adapter前会先调用validateAdapterpackages/enzyme/src/validateAdapter.js 会依次校验adapter 缺失、传入构造函数而非实例、传入普通函数、实例未继承EnzymeAdapter基类四种错误均抛出带To configure an adapter, you should call Enzyme.configure({ adapter: new Adapter() })提示的明确异常adapter 与 React 版本必须匹配仓库在packages/下提供了 enzyme-adapter-react-13、enzyme-adapter-react-14、enzyme-adapter-react-15、enzyme-adapter-react-16 等一整套按 React 版本划分的适配器包例如 enzyme-adapter-react-16/src/index.js 实际导出ReactSixteenAdapter其实现基于react-dom、react-test-renderer与react-is等配套库。9.2shallow/mount的渲染差异决定了 DOM 需求框架选型时最重要的判断标准是是否调用mount。源码层面packages/enzyme/src/shallow.js 的shallow只是new ShallowWrapper(node, null, options)——只渲染一层、不需要 DOM而 packages/enzyme/src/mount.js 的mount则构造ReactWrapper会真实挂载到 document。因此只用shallow的场景如 Tape/AVA/Lab 示例在纯 Node 环境即可运行只要用到mount就必须先按第三节方式加载 JSDOMNode 环境或运行在真实浏览器中Karma 场景。9.3 集成模式速查表测试框架环境必须配置是否需 DOMJest≥15NodesetupFilesAfterEnv指向 setup 文件配置 Adapter用mount时需要 JSDOMJest15Node额外设置unmockedModulePathPatterns同上MochaNodemocha --require setup.js加载 JSDOM setup用mount时需要Karma真实浏览器setup 文件配置 Adapter 并重新导出 enzyme浏览器自带 DOMJSDOM 通用Node在首次加载 React 前注入全局window/document自身即 DOM 方案Webpack/Browserify浏览器enzyme 3.0 无需特殊配置浏览器自带React NativeNode 模拟 DOMAdapter JSDOMtestID选择器mount时需要Lab CodeNodesetup 中配置 Adapter用mount时需要Tape / AVANode测试前configure({ adapter })用mount时需要十、延伸阅读各 API 的详细用法见 docs/api/README.md其中 ReactWrapper 与 ShallowWrapper 两个目录收录了全部方法文档安装与版本选择见 docs/installation/README.md针对 React 13/14/15/16 的说明分别位于 react-013.md、react-014.md、react-15.md、react-16.md常见问题排查见 docs/common-issues.md术语对照见 docs/GLOSSARY.md仓库packages/enzyme-test-suite/test/下的 ReactWrapper-spec.jsx 与 ShallowWrapper-spec.jsx 提供了大量可参考的集成测试用例。赞分享测试前端【免费下载链接】enzymeJavaScript Testing utilities for React项目地址https://gitcode.com/gh_mirrors/en/enzyme点击查看免费下载相关推荐KotlinMultiPlatform 常见问题解决跨平台开发中的坑与解决方案KotlinMultiPlatform 常见问题解决跨平台开发中的坑与解决方案 Kotlin MultiPlatformKMP是一项强大的技术允许开发者视频超分辨率技术瓶颈BasicVSR如何突破传统算法的时空限制视频超分辨率技术瓶颈BasicVSR如何突破传统算法的时空限制 在视频内容创作和媒体处理领域低分辨率视频的质量提升一直是一个技术难题。传统视频超分辨率方人工智能计算机视觉视频处理terminal-browser工作原理解密如何在终端中渲染网页像素terminal browser工作原理解密如何在终端中渲染网页像素 terminal browser是一款创新的终端浏览器它能够直接在你现有的终端中渲染创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

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

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