如何在Jest测试中修改React useState Hook的状态值?
问题排查与解决方案
你现有代码的错误点如下:
- 拼写错误:自定义初始状态里的键写为
signUpTyp,缺少末尾的e,和实际定义的signUpType不匹配 - 结构错误:给
useState返回的第一个参数额外套了一层数组,原state是纯对象,会导致组件内读取form.signUpType失败 - 模拟的
setForm为空对象,组件后续如果调用更新方法会直接报错 - 直接覆盖
React.useState的方式不严谨,如果组件内有多个useState,所有hook都会被替换为同一个模拟值,逻辑完全混乱
推荐方案:模拟用户操作切换注册类型(无需mock hook)
单元测试优先选择贴近用户实际使用的写法,避免和组件内部实现耦合。你页面上肯定存在切换手机号/邮箱注册的交互按钮,直接模拟点击即可:
it("Email validation", () => { const email = "sample.s@abc.com"; const tree = mount( <Provider store={configureStore}> <SignIn /> </Provider> ); // 替换为你实际的切换按钮选择器,优先用data-testid标记测试元素 tree.find('[data-testid="switch-to-email-btn"]').simulate('click'); // 验证邮箱输入框显示、值符合预期 expect(tree.find('CustomTextInput[attrName="email"]').exists()).toBeTruthy(); expect(tree.find('CustomTextInput[attrName="email"]').prop("value")).toBe(""); expect(email).toMatch(/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,})+$/); });
该方案不受内部state实现方式影响,哪怕后续你把useState替换为useReducer,测试用例也不需要修改,可靠性更高。
可选方案:直接mock useState
如果确实需要强制修改初始state值,可按以下正确写法实现:
it("Email validation", () => { const email = "sample.s@abc.com"; // 构造和原state完全一致的模拟数据 const mockForm = { email: "", phoneNumber: "", password: "", signUpType: SignUpType.EMAIL }; // 用spyOn模拟单个useState调用,避免影响其他测试 const useStateSpy = jest.spyOn(React, 'useState') // 按组件内useState调用顺序,第一个调用的就是form state,返回模拟值 .mockReturnValueOnce([mockForm, jest.fn()]); const tree = mount( <Provider store={configureStore}> <SignIn /> </Provider> ); expect(tree.find('CustomTextInput[attrName="email"]').prop("value")).toBe(""); expect(email).toMatch(/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,})+$/); // 测试完成后清除mock,避免污染其他用例 useStateSpy.mockRestore(); });
注意:如果组件内有多个useState,需要按调用顺序依次用mockReturnValueOnce设置每个hook的返回值,顺序错误会导致取值混乱。
内容的提问来源于stack exchange,提问作者Anu
相关产品推荐
相关产品推荐

