十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

enzyme 深入指南:使用 `.setContext()` 驱动 React Context 变化测试

enzyme 深入指南:使用 `.setContext()` 驱动 React Context 变化测试 enzyme 深入指南使用.setContext()驱动 React Context 变化测试【免费下载链接】enzymeJavaScript Testing utilities for React项目地址: https://gitcode.com/gh_mirrors/en/enzyme导读在 enzyme 的浅渲染Shallow Rendering测试中如何模拟组件运行期间 Context 发生变化的场景.setContext(context)正是为此设计的 API它直接改写根组件的 context 并触发重新渲染让开发者可以在一次测试里连续验证组件在不同 context 下的渲染结果。本文以 docs/api/ShallowWrapper/setContext.md 为骨架结合 ShallowWrapper 源码 与 共享测试套件完整讲解其签名、前置条件、底层实现原理、生命周期行为与常见陷阱读完即可在真实测试中熟练运用。方法签名与作用.setContext(context) Self.setContext(context)用于设置根组件的 context并立即重新渲染。官方文档的定位是A method that sets the context of the root component, and re-renders. Useful for when you are wanting to test how the component behaves over time with changing contexts.—— 即当你想验证组件在随时间变化的 context 下如何表现时使用它。该方法只接受一个参数参数类型说明contextObject要合并进当前 context 的新对象返回值为ShallowWrapper自身即this因此天然支持链式调用例如wrapper.setContext(a).setContext(b)。需要特别注意的是.setContext()只能被调用在同时是根实例root的 wrapper 上对find()/children()等得到的子 wrapper 调用会直接抛出错误详见下文前置条件与报错。完整可运行示例文档给出的示例展示了多次改变 context每次都重新断言渲染结果的完整流程。为了让它可以直接复制运行这里补齐了完整的导入语句import React from react; import PropTypes from prop-types; import { shallow } from enzyme; import { expect } from chai; function SimpleComponent(props, context) { const { name } context; return div{name}/div; } SimpleComponent.contextTypes { name: PropTypes.string, };const context { name: foo }; const wrapper shallow(SimpleComponent /, { context }); expect(wrapper.text()).to.equal(foo); wrapper.setContext({ name: bar }); expect(wrapper.text()).to.equal(bar); wrapper.setContext({ name: baz }); expect(wrapper.text()).to.equal(baz);执行流程解读shallow(SimpleComponent /, { context })—— 在创建 wrapper 时必须通过options.context传入初始 context这是后续调用.setContext()的硬性前提第一次断言验证初始渲染输出为foowrapper.setContext({ name: bar })改写 context 并触发重渲染输出变为bar再次调用.setContext({ name: baz })输出继续变为baz证明 context 可以连续多次更新。这段代码在仓库的共享测试套件 setContext.jsx 中有完全对应的用例sets context for a component multiple times并且该测试同时覆盖了函数式组件SFC与createClass类组件两种写法。前置条件与报错行为Common Gotchas文档明确列出两条使用铁律二者缺一不可否则调用会失败1. 创建 wrapper 时必须传入context选项.setContext()can only be used on a wrapper that was initially created with a call toshallow()that includes acontextspecified in the options argument.如果shallow(Component /)没有携带{ context }选项后续调用.setContext()会抛出错误。从 ShallowWrapper 源码 可以看到这一校验逻辑setContext(context) { if (this[ROOT] ! this) { throw new Error(ShallowWrapper::setContext() can only be called on the root); } if (!this[OPTIONS].context) { throw new Error(ShallowWrapper::setContext() can only be called on a wrapper that was originally passed a context option); } return this.rerender(null, context); }也就是说OPTIONS.context为假值时直接拒绝执行。测试套件中的throws if it is called when wrapper didn’t include context用例验证的正是这条错误信息ShallowWrapper::setContext() can only be called on a wrapper that was originally passed a context option。2. 根组件必须声明contextTypesThe root component you are rendering must have acontextTypesstatic property.React 的旧式 Context 机制要求组件通过静态属性contextTypes声明自己需要读取哪些 context 字段在本仓库支持的 React 版本范围内这是传递 context 的标准方式。示例中的SimpleComponent.contextTypes { name: PropTypes.string }即为此目的测试套件中的 SFC 写法SFC.contextTypes { name: PropTypes.string }同理。3. 只能在根 wrapper 上调用对非根 wrapper例如wrapper.find(main)返回的子 wrapper调用.setContext()会抛出ShallowWrapper::setContext() can only be called on the root。测试用例throws when not called on the root专门覆盖了这一点先用{ context: { name: main / } }创建 wrapper再对find(main)的结果调用setContext()断言抛错。底层实现原理从setContext到rerender.setContext()本身的实现非常薄真正的重渲染逻辑委托给了 rerender(props, context)。这条调用链揭示了它的完整行为setContext(context) └─ 校验 ROOT 与 OPTIONS.context └─ rerender(null, context) ├─ 将新 context 合并进 this[OPTIONS].context ├─ 在 batchedUpdates 中调用 this[RENDERER].render(UNRENDERED, nextContext, ...) ├─ 依据 shouldComponentUpdate 的返回值决定是否触发 componentDidUpdate └─ this.update() 刷新 wrapper 内部节点关键源码细节ShallowWrapper.js#L588-L605const prevContext instance.context || this[OPTIONS].context; const nextContext context || prevContext; if (context) { this[OPTIONS] { ...this[OPTIONS], context: nextContext }; }新 context 会被整体写入OPTIONS.context之后的读取操作如.context([key])都能拿到更新后的值渲染器在batchedUpdates内以nextContext重新渲染未渲染的根元素UNRENDERED若未开启disableLifecycleMethods重渲染会走完整的生命周期路径shouldComponentUpdate→按需componentDidUpdate这与 React 真实的 context 更新行为保持了一致性。值得注意的细节rerender内部对prevProps取自instance.props || this[UNRENDERED].props对 SFC 场景React 16 中实例为null也做了兼容处理这正是测试套件中 SFC 用例能够通过的原因。生命周期联动setContext 会触发什么.setContext()不只是替换渲染数据它还会像真实的 context 更新一样驱动生命周期方法。测试套件 setContext.jsx 中calls componentWillReceiveProps when context is updated用例通过 spy 记录调用序列断言结果为[render, componentWillReceiveProps, render]即在重新渲染时componentWillReceiveProps会被调用一次。更细致地对于 React 16.3calls componentWillReceiveProps and UNSAFE_componentWillReceiveProps when context is updated用例验证了新旧两个生命周期同时被触发[render, componentWillReceiveProps, UNSAFE_componentWillReceiveProps, render]该用例还顺带验证了两点更新后wrapper.context(foo)能读到新值baz且wrapper.debug()输出的渲染树已包含新 context 渲染出的baz文本节点。这提醒我们在升级 React 版本时.setContext()的测试行为会随 React 生命周期 API 的演进而变化需要留意UNSAFE_前缀方法的兼容性。与.setState()、.setProps()的配套使用.setContext()与.setState(state[, callback])、.setProps(nextProps[, callback])共同构成 enzyme 驱动根组件随时间变化的三驾马车.setState()更新内部 state.setProps()更新根组件 props会触发componentWillReceiveProps.setContext()更新根组件 context同样会触发componentWillReceiveProps见上文。三者都遵循相同的约束只能作用于根 wrapper都返回this以支持链式调用且都通过rerender统一完成重渲染。在测试props 与 context 联动变化的场景时可以将它们串联使用例如先setProps({ foo: bar })再setContext({ theme: dark })验证组件在多重外部输入变化下的综合表现。常见使用场景小结Context 驱动的 UI 变化主题切换、国际化文案、认证状态等依赖 context 展示不同内容的组件可以连续多次.setContext()断言每一步输出生命周期验证配合 sinon spy 验证componentWillReceiveProps/UNSAFE_componentWillReceiveProps在 context 更新时是否按预期触发参考 setContext.jsx 中的 spy 写法回归保护在升级 React 版本本仓库覆盖 0.13 ~ 16.x 各版本适配器时用同一套.setContext()测试锁定组件行为及时发现生命周期 API 变更带来的影响。最后再次强调两条使用前提shallow()时必须携带{ context }选项且根组件必须声明contextTypes—— 满足这两个条件后.setContext()就能像.setProps()一样轻量、可链式地驱动 context 变化让随时间变化的组件行为测试变得清晰而稳定。【免费下载链接】enzymeJavaScript Testing utilities for React项目地址: https://gitcode.com/gh_mirrors/en/enzyme创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表