如何在React中对useFormik的onSubmit进行单元测试?
如何测试React组件中Formik表单的提交功能
我有一个名为Subscribe.js的React组件,它依赖自定义HookuseSubscribe处理业务逻辑。该Hook通过useFormik为表单配置了验证规则和onSubmit提交逻辑,现在需要测试表单的提交功能。
我的测试尝试
it("should complete form submission", async () => { const handleSubmit = jest.fn(); render(<Subscribe />); const inputElement = screen.getByPlaceholderText("Enter your email"); userEvent.type(inputElement, "abc@gmail.com"); const submitButton = screen.getByRole("button", { name: /submit/i }); userEvent.click(submitButton); await waitFor(() => { expect(handleSubmit).toHaveBeenCalledTimes(1); }); });
我的组件代码
Subscribe.js
import React from "react"; import { Button, Form } from "react-bootstrap"; import useSubscribe from "./../../hooks/useSubscribe"; import { FormField } from "./../FormField/FormField"; function Subscribe() { const { formik, isLoading } = useSubscribe(); return ( <Form onSubmit={formik.handleSubmit}> <FormField type="Email" className="w-100" placeholder="Enter your email" isInvalid={formik.errors.email} isValid={formik.touched.email && !formik.errors.email} error={formik.errors.email} {...formik.getFieldProps("email")} /> <Button type="submit" variant="primary" size="lg" className="btn btn-primary btn-lg px-4 mb-2" onClick={formik.handleSubmit} >Submit </Button> </Form> ); } export default Subscribe;
useSubscribe Hook
import { useFormik } from "formik"; import * as Yup from "yup"; const useSubscribe = () => { const initialValues = { email: "", }; const formik = useFormik({ initialValues: initialValues, validationSchema: Yup.object({ email: Yup.string().email("Invalid Email").required("Email is required"), }), onSubmit: async (values) => { // 调用后端接口,传入values } }); return { formik, }; }; export default useSubscribe;
问题分析与解决方案
你的测试代码里定义了handleSubmit这个mock函数,但它并没有和组件实际使用的formik.handleSubmit关联起来,所以测试永远不会通过。下面提供两种可行的测试方案:
方案1:Mock自定义HookuseSubscribe
直接mock整个useSubscribe,让它返回带有mock提交函数的formik对象,这样可以验证提交函数是否被正确调用:
import useSubscribe from "./../../hooks/useSubscribe"; // Mock自定义Hook jest.mock("./../../hooks/useSubscribe"); it("should trigger form submission with correct email", async () => { // 创建mock的handleSubmit函数 const mockHandleSubmit = jest.fn(); // 让Hook返回我们的mock对象 useSubscribe.mockReturnValue({ formik: { handleSubmit: mockHandleSubmit, getFieldProps: jest.fn(() => ({ name: "email", value: "", onChange: jest.fn(), onBlur: jest.fn() })), errors: {}, touched: {} } }); render(<Subscribe />); // 输入邮箱并点击提交 const inputElement = screen.getByPlaceholderText("Enter your email"); userEvent.type(inputElement, "abc@gmail.com"); const submitButton = screen.getByRole("button", { name: /submit/i }); userEvent.click(submitButton); // 验证提交函数被调用 await waitFor(() => { expect(mockHandleSubmit).toHaveBeenCalled(); }); });
方案2:Mock后端请求并验证参数
如果想测试实际的提交逻辑(比如是否正确调用后端接口),可以mock网络请求库(比如axios),然后验证请求是否携带了正确的参数:
假设你的onSubmit里用axios调用后端:
// useSubscribe.js里的onSubmit onSubmit: async (values) => { await axios.post("/api/subscribe", values); }
测试代码:
import axios from "axios"; // Mock axios jest.mock("axios"); it("should submit the correct email to backend", async () => { render(<Subscribe />); // 输入邮箱并提交 const inputElement = screen.getByPlaceholderText("Enter your email"); userEvent.type(inputElement, "abc@gmail.com"); const submitButton = screen.getByRole("button", { name: /submit/i }); userEvent.click(submitButton); // 验证axios.post被调用,且参数正确 await waitFor(() => { expect(axios.post).toHaveBeenCalledWith( "/api/subscribe", { email: "abc@gmail.com" } ); }); });
额外优化点
你的组件里,<Button>同时设置了type="submit"和onClick={formik.handleSubmit},这会导致提交逻辑被触发两次(一次是表单的onSubmit,一次是按钮的onClick)。建议移除按钮的onClick属性,只保留表单的onSubmit即可,避免重复调用。
内容的提问来源于stack exchange,提问作者Ali Raza
相关产品推荐
相关产品推荐

