使用React Testing Library测试useFormik Hook表单时无法触发onSubmit的问题求助
问题根源分析
你遇到的核心问题是测试用例里的mockSubmit完全没和组件内部的Formik onSubmit逻辑关联起来。你的WithMaterialUI组件里硬编码了自己的onSubmit函数(弹出alert),而测试里定义的mockSubmit从未被组件调用,所以自然不会触发预期的断言。
解决方案
这里提供两种可行的解决思路,你可以根据自己的需求选择:
方案1:将onSubmit作为组件Props传入(推荐,更符合可测试性设计)
把组件改成接受外部传入的onSubmit,这样测试时就能轻松注入mock函数:
修改组件代码
export const WithMaterialUI = ({ onSubmit }) => { const formik = useFormik({ initialValues: { email: "foobar@example.com" }, validationSchema: validationSchema, onSubmit: (values) => { // 保留原有业务逻辑(如果需要) alert(JSON.stringify(values, null, 2)); // 调用外部传入的回调,方便测试 onSubmit(values); } }); // 其余代码保持不变 return ( <div> <form onSubmit={formik.handleSubmit} data-testid="form"> <TextField data-testid="email" fullWidth id="email" name="email" label="Email" value={formik.values.email} onChange={formik.handleChange} error={formik.touched.email && Boolean(formik.errors.email)} helperText={formik.touched.email && formik.errors.email} /> <Button color="primary" variant="contained" fullWidth type="submit"> Submit </Button> </form> </div> ); };
如果不需要原有alert逻辑,也可以直接复用props的onSubmit:
export const WithMaterialUI = ({ onSubmit }) => { const formik = useFormik({ initialValues: { email: "foobar@example.com" }, validationSchema: validationSchema, onSubmit }); // 其余代码不变 };
修改测试用例
const mockSubmit = jest.fn(); describe("Test for formik onSubmit", () => { it("should handle form submission", async () => { const { getByTestId } = render(<WithMaterialUI onSubmit={mockSubmit} />); const email = getByTestId("email"); const form = getByTestId("form"); fireEvent.change(email, { target: { value: "abc@gmail.com" } }); fireEvent.submit(form); await waitFor(() => { // 不仅验证被调用,还可以校验传入的参数是否正确 expect(mockSubmit).toHaveBeenCalledWith({ email: "abc@gmail.com" }); }); }); });
方案2:直接测试组件原有行为(无需修改组件结构)
如果你不想调整组件设计,也可以直接测试组件的原生行为(比如alert是否被触发):
describe("Test for formik onSubmit", () => { it("should trigger alert with correct values on submission", async () => { // 模拟window.alert方法 window.alert = jest.fn(); const { getByTestId } = render(<WithMaterialUI />); const form = getByTestId("form"); fireEvent.submit(form); await waitFor(() => { expect(window.alert).toHaveBeenCalledWith( JSON.stringify({ email: "foobar@example.com" }, null, 2) ); }); // 测试完成后恢复原方法,避免影响其他测试 window.alert.mockRestore(); }); });
额外小提示
测试里的act()包裹其实可以省略——fireEvent和render已经自动处理了React的更新流程,waitFor也会正确处理异步逻辑。
内容的提问来源于stack exchange,提问作者krsna
相关产品推荐
相关产品推荐

