如何用Jest+Enzyme测试Props传入状态更新函数的React组件
核心问题
你当前的测试思路存在偏差,不需要模拟React内置的useState Hook,问题本质是你对受控组件的测试逻辑理解错了:
- 你写的Switch是标准受控组件,状态所有权属于父组件,组件本身只负责接收
state展示状态、触发点击时调用父组件传入的setState,自身内部不应该持有同名状态,你在测试里调用wrapper.state("state")读取组件内部状态,从测试逻辑上就错了。 - 你把
wrapper.simulate、初始状态读取的逻辑写在了describe块的根作用域,这些代码会在所有用例执行前、beforeEach挂载组件之前运行,执行时序完全错误,大概率会拿不到挂载好的组件实例。 - 你挂载组件时没有传入必填的
state、setState两个props,不符合组件本身的使用要求。
正确测试方案
受控组件的单元测试不需要mock Hook,只需要验证两个核心行为即可:
- 组件接收到不同的
stateprop时,渲染的UI状态符合预期 - 用户点击组件时,会调用传入的
setStateprop,参数为当前state取反后的值
基础写法(推荐,执行快、用例隔离性好)
直接传入mock函数和可控的state值即可,不需要额外包裹组件:
import Switch from "./index"; import { mount, ReactWrapper } from "enzyme"; describe("[UNIT] Testing Switch component", () => { let wrapper: ReactWrapper; const mockSetState = jest.fn(); beforeEach(() => { // 每个用例执行前重置mock记录,传入符合要求的props mockSetState.mockClear(); wrapper = mount(<Switch state={false} setState={mockSetState} />); }); it('should trigger setState with toggled value on click', () => { wrapper.simulate("click"); // 验证setState被正确调用,参数为当前state的反值 expect(mockSetState).toHaveBeenCalledTimes(1); expect(mockSetState).toHaveBeenCalledWith(true); }); it('should render matching UI based on state prop', () => { // 初始state为false时的UI校验,根据你组件的实际DOM结构调整选择器 expect(wrapper.find('[aria-checked="true"]').exists()).toBe(false); // 更新传入的state prop,验证UI同步更新 wrapper.setProps({ state: true }); wrapper.update(); expect(wrapper.find('[aria-checked="true"]').exists()).toBe(true); }); });
真实场景模拟写法
如果你需要模拟真实父组件的状态流转,可以写一个极简的测试父组件,直接用真实的useState,不需要任何mock:
import { useState } from "react"; import { act } from "react-dom/test-utils"; // 模拟实际业务中使用Switch的父组件 const TestContainer = () => { const [switchVal, setSwitchVal] = useState(false); return <Switch state={switchVal} setState={setSwitchVal} />; }; it('should toggle state correctly in real parent-child flow', () => { const wrapper = mount(<TestContainer />); // 校验初始状态 expect(wrapper.find(Switch).prop("state")).toBe(false); // 触发点击,状态更新需要包裹在act中避免警告 act(() => { wrapper.find(Switch).simulate("click"); }); wrapper.update(); // 校验状态更新后的值 expect(wrapper.find(Switch).prop("state")).toBe(true); });
注意:所有触发React状态更新的操作,建议包裹在
act()回调中执行,保证状态更新完成、DOM重渲染后再执行断言,避免出现测试结果不稳定的问题。
内容的提问来源于stack exchange,提问作者Louis Lecouturier
相关产品推荐
相关产品推荐

