` 详解:读取浅渲染根节点 Prop 的正确姿势)
测试前端【免费下载链接】enzymeJavaScript Testing utilities for React项目地址https://gitcode.com/gh_mirrors/en/enzyme点击查看免费下载在 enzyme 的 ShallowWrapper API 中.prop(key)是获取浅渲染结果中根节点root node指定 prop 值的核心方法。无论是断言组件接收到的 props、取出事件回调手动触发还是校验 DOM 属性的映射结果.prop(key)都是高频使用的测试工具。本文将基于仓库文档 docs/api/ShallowWrapper/prop.md结合 ShallowWrapper 源码 与 共享测试用例完整讲解该方法的使用规则、参数语义、与instance().props的关键区别以及底层实现原理。方法签名与核心语义.prop(key) Any返回值Any即包装器wrapper根节点上、由key指定的 prop 值返回值类型与传入 prop 时的类型完全一致——字符串返回字符串函数返回函数对象返回对象引用约束条件必须是一个单节点包装器single-node wrapper即调用find、filter等操作后包装器内只能包含恰好 1 个节点否则会抛出运行时错误。参数说明参数类型必填含义keyString是prop 名称等价于读取根节点的this.props[key]或props[key]也就是说wrapper.prop(foo)与直接读取 props 对象的wrapper.props().foo结果一致——事实上从源码看prop正是对props()的薄封装// packages/enzyme/src/ShallowWrapper.js#L1309-L1311 prop(propName) { return this.props()[propName]; }而props()又会经过单节点校验后调用 RSTReact Standard Tree遍历工具函数取 props// packages/enzyme/src/ShallowWrapper.js#L1173-L1175 props() { return this.single(props, propsOfNode); }// packages/enzyme/src/RSTTraversal.js#L9-L11 export function propsOfNode(node) { return (node node.props) || {}; }propsOfNode对空节点做了容错返回{}因此对不存在的 prop 读取会得到undefined而非抛错这一点在断言时需要注意。单节点约束single机制.prop(key)要求包装器只包含一个节点这一约束由 ShallowWrapper 内部通用的single辅助方法保证。当包装器中的节点数量不为 1 时会抛出如下错误// packages/enzyme/src/ShallowWrapper.js#L1647-L1654 single(name, fn) { const fnName typeof name string ? name : unknown; const callback typeof fn function ? fn : name; if (this.length ! 1) { throw new Error(Method “${fnName}” is meant to be run on 1 node. ${this.length} found instead.); } return callback.call(this, this.getNodeInternal()); }错误信息形如Method prop is meant to be run on 1 node. 3 found instead.。因此在调用.prop()前应确保使用first()、.at(index)等手段将包装器收敛为单节点尤其是对.find()可能命中多个元素的情况。官方示例prop 读取、事件回调触发与状态联动仓库文档提供了一个完整的实战示例同时演示了三件事读取根节点 prop、通过取出的回调触发事件、以及验证 state 的联动更新。组件定义如下import PropTypes from prop-types; import ValidateNumberInputComponent from ./ValidateNumberInputComponent; class MyComponent extends React.Component { constructor(...args) { super(...args); this.state { number: 0, }; this.onValidNumberInput this.onValidNumberInput.bind(this); } onValidNumberInput(e) { const number e.target.value; if (!number || typeof number number) { this.setState({ number }); } } render() { const { includedProp } this.props; const { number } this.state; return ( div classNamefoo bar includedProp{includedProp} ValidateNumberInputComponent onChangeHandler{onValidNumberInput} number{number} / /div ); } } MyComponent.propTypes { includedProp: PropTypes.string.isRequired, };注意示例中onValidNumberInput在 JSX 中直接使用未加this.此处省略号用于示意其绑定方式实际运行请按 React 规范在构造函数中完成this.onValidNumberInput this.onValidNumberInput.bind(this)或使用 class 属性/箭头函数写法。第一步读取根节点 propconst wrapper shallow(MyComponent includedPropSuccess! excludedPropIm not included /); expect(wrapper.prop(includedProp)).to.equal(Success!);这里wrapper.prop(includedProp)读到的Success!是MyComponent 渲染出的根 DOM 节点div上的includedProp属性它由render()中的{...}展开传递而来。第二步取出回调 prop 并手动触发const validInput 1; wrapper.find(ValidateNumberInputComponent).prop(onChangeHandler)(validInput); expect(wrapper.state(number)).to.equal(validInput); const invalidInput invalid input; wrapper.find(ValidateNumberInputComponent).prop(onChangeHandler)(invalidInput); expect(wrapper.state(number)).to.equal(0);find(...)会返回包含子组件节点的包装器再对其调用.prop(onChangeHandler)即可取出传入子组件的函数 prop并直接调用模拟用户输入事件。由于触发的是真实绑定的组件方法state 会随之更新传入合法数字1时setState({ number: 1 })生效传入非法字符串invalid input时onValidNumberInput内的校验!number || typeof number number不通过state 保持0。这种“取回调、手动触发、断言 state”的模式是浅渲染下测试组件间交互的常用手段也是.invoke()方法存在的意义——它封装了“取出函数 prop 并调用、随后更新根包装器”的流程。源码中invoke正是基于this.prop(propName)实现的见 ShallowWrapper.js#L1320-L1332。第三步理解undefined与instance().props的区别console.log(wrapper.prop(includedProp)); // Success! console.log(wrapper.prop(excludedProp)); // undefined console.log(wrapper.instance().props.excludedProp); // Im not included这是整个方法最容易踩坑的地方excludedProp虽然在渲染时被传给了MyComponent 组件本身但由于 MyComponent 并未把该 prop 透传到其渲染出的根div上因此wrapper.prop(excludedProp)返回undefined根 DOM 节点上没有该 propwrapper.instance().props.excludedProp返回Im not included组件实例上完整保留了所有传入的 props。核心差异ShallowWrapper 的.prop()与 ReactWrapper 的.prop()官方文档特别在 NOTE 中强调When called on a shallow wrapper,.prop(key)will return values for props on the root node that the componentrenders, not the component itself. To return the props for the entire React component, usewrapper.instance().props.这句话道出了浅渲染语义的精髓ShallowWrapperwrapper包装的是组件的根渲染节点即render()返回的最外层元素因此.prop(key)读取的是渲染结果上的 props而非传入组件自身的 propsReactWrappermount后的wrapper.prop(foo)直接读取组件根节点的 prop见 docs/api/ReactWrapper/prop.md 的示例const wrapper mount(MyComponent foo{10} /); expect(wrapper.prop(foo)).to.equal(10);。这一差异在共享测试套件中有非常明确的对照验证见 packages/enzyme-test-suite/test/shared/methods/prop.jsxclass Foo extends React.Component { render() { const { bar, foo } this.props; return div className{bar} id{foo} /; } } // 挂载mount模式下读取的是组件自身的 props const wrapper Wrap(Foo foohi barbye /); expect(wrapper.prop(foo)).to.equal(hi); // 传入组件的 props expect(wrapper.prop(className)).to.equal(undefined); // 浅渲染shallow模式下读取的是渲染出的根节点上的 props const wrapperRendered WrapRendered(Foo foohi barbye /); expect(wrapperRendered.prop(className)).to.equal(bye); // 渲染结果上的 props expect(wrapperRendered.prop(foo)).to.equal(undefined); // 组件自身的 props 不在此处测试还验证了.prop()可以在内层节点上使用const wrapper Wrap(( div classNamebax div classNamebaz onClick{fn} / div classNamefoo idfooId / /div )); expect(wrapper.find(.baz).prop(onClick)).to.equal(fn); expect(wrapper.find(.foo).prop(id)).to.equal(fooId);只要find等操作得到的包装器是单节点.prop()就能取到该节点的任意 prop包括函数类型的onClick这也说明.prop()并非只能用于根节点而是可以作用于包装器当前指向的任意单节点。常见使用场景与注意事项1. 断言组件渲染结果是否正确接收/透传 propsexpect(wrapper.prop(className)).to.equal(foo bar);结合hasClass或prop直接断言根节点的 class、id、data-* 等属性是浅渲染测试中最常见的断言形式。2. 读取不存在的 prop 时返回undefined由于propsOfNode对缺失字段不做特殊处理wrapper.prop(不存在的key)稳定返回undefined不会抛错。若想断言“组件确实收到了某个 prop 但未透传到渲染结果”应改用wrapper.instance().props参见.instance() ReactComponent。3. 配合invoke调用函数 prop当函数 prop 需要被调用且要求调用后同步更新包装器时优先使用wrapper.invoke(propName)。其实现会先取this.prop(propName)若值不是函数则抛出TypeError: ShallowWrapper::invoke() requires the name of a prop whose value is a function并在调用后执行this[ROOT].update()刷新根包装器避免手动update()。4. 单节点约束对.find()命中多个元素的结果直接调用.prop()会抛出Method prop is meant to be run on 1 node. N found instead.请先使用.first()或.at(index)收敛。5. 不要混淆.prop()、.state()与.context().prop(key)读取根渲染节点的 propsthis.props[key].state([key])读取类组件的 state仅能在根包装器上对类组件调用见.state([key]) Any.context([key])读取组件上下文见.context([key]) Any。三者分别对应 props / state / context 三条数据通路在浅渲染测试中经常组合使用用于完整校验组件的数据流。相关方法.props() Object一次性返回根渲染节点的全部 props 对象prop(key)等价于props()[key].state([key]) Any读取或按 key 读取类组件 state.context([key]) Any按 key 读取组件 context.instance() ReactComponent获取组件实例通过wrapper.instance().props读取组件自身收到的全部 props.invoke(propName) Any取出函数 prop 并调用内部基于prop()实现.renderProp(propName) Function渲染 render-prop 模式下的子内容。对于完整挂载渲染场景下的 prop 读取语义可对照阅读 docs/api/ReactWrapper/prop.md并参考 ShallowWrapper-spec 与 ReactWrapper-spec 中的更多行为验证。小结.prop(key)是 ShallowWrapper 中读取节点 props 的最直接入口它的语义始终围绕“包装器当前指向的渲染节点”展开必须单节点调用否则抛错浅渲染下返回的是组件渲染出的根节点上的 props而非传入组件自身的 props后者请用instance().props对不存在的 key 返回undefined函数类型的 prop 可以取出后手动调用或交由invoke()封装处理。理解“渲染结果 vs 组件自身”这一差异是正确使用 ShallowWrapper 进行浅渲染测试的分水岭也是避免写出“断言永远为真/假”的脆弱测试的关键。赞分享测试前端【免费下载链接】enzymeJavaScript Testing utilities for React项目地址https://gitcode.com/gh_mirrors/en/enzyme点击查看免费下载相关推荐enzyme ReactWrapper .context([key]) 详解读取根组件 React Context 的正确姿势enzyme ReactWrapper .context key 详解读取根组件 React Context 的正确姿势 导读 在 enzyme 的 moun测试前端Enzyme ReactWrapper .state([key]) 方法完全指南读取组件 State 的正确姿势Enzyme ReactWrapper .state key 方法完全指南读取组件 State 的正确姿势 导读 在基于 Enzyme 编写 React 组件测试前端Enzyme 中 ShallowWrapper.renderProp() 的完整指南在浅渲染下测试 Render Prop 组件Enzyme 中 ShallowWrapper.renderProp 的完整指南在浅渲染下测试 Render Prop 组件 导读 在 React 测试中测试前端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考