You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 17:10:32