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

React Hook Form表单测试报错:jest.fn()调用次数不匹配

问题排查与解决方案

核心问题分析

你的测试失败主要由以下几个关键问题导致:


1. 组件内部覆盖了传入的onSubmit回调

在ContactForm组件中,你接收了props.onSubmit,但紧接着又声明了同名的内部变量onSubmit,直接覆盖了外部传入的jest mock函数,导致测试中的onSubmit永远不会被调用:

// 组件中错误的写法
export function ContactForm({ onSubmit }: ContactFormProps) {
  // ...
  // 这里的onSubmit覆盖了props传入的回调
  const onSubmit: SubmitHandler<FormSchemaType> = data => {
    ...
  };
}

2. fireEvent.input调用方式错误

你直接传入字符串作为第二个参数,正确的写法应该是传递包含target.value的对象:

// 错误写法
fireEvent.input(emailInput, "abc@abc.com");
// 正确写法
fireEvent.input(emailInput, { target: { value: "abc@abc.com" } });

3. 可能存在表单校验未通过的情况

由于使用了zodResolver,如果测试输入不符合FormSchema的校验规则,handleSubmit会直接阻断提交流程,不会触发回调。

4. 测试中多余的FormProvider包装

ContactForm内部已经创建了自己的useForm实例并通过FormProvider提供上下文,测试外层额外包装的FormProvider会造成上下文冲突(虽然不是核心问题,但应该移除)。


修复步骤

第一步:修正组件的onSubmit逻辑

将内部处理函数改名,确保调用外部传入的回调:

export function ContactForm({ onSubmit: submitCallback }: ContactFormProps) {
  const methods = useForm<FormSchemaType>({
    resolver: zodResolver(FormSchema),
  });
  const { handleSubmit, reset } = methods;
  
  // 内部处理逻辑后调用外部传入的回调
  const handleFormSubmit: SubmitHandler<FormSchemaType> = (data) => {
    // ...你的业务逻辑
    submitCallback(data); // 触发外部传入的onSubmit
  };

  return (
    <>
      <FormProvider {...methods}>
          <FormWrapper>
            <form onSubmit={handleSubmit(handleFormSubmit)}>
              ....
            </form>
          </FormWrapper>
      </FormProvider>
    </>
  );
}

第二步:修正测试代码

describe("Form", () => {
  const onSubmit = jest.fn();

  const renderForm = () => {
    customRender(
      <ContactForm onSubmit={onSubmit} />,
      { withRouter: true }
    );
  };

  it("successful submit", async () => {
    renderForm();
    const emailInput = screen.getByRole("textbox", { name: /email/i });
    const fNameInput = screen.getByRole("textbox", { name: /fname/i });
    const submitButton = screen.getByRole("button", { name: /submit/i });

    // 正确设置输入值
    fireEvent.input(emailInput, { target: { value: "abc@abc.com" } });
    fireEvent.input(fNameInput, { target: { value: "Alex" } });
    
    // 触发提交
    fireEvent.click(submitButton);

    // 如果组件的submit是异步操作,必须用waitFor等待回调执行
    await waitFor(() => {
      expect(onSubmit).toHaveBeenCalledTimes(1);
    });
  });
});

额外注意事项

  • 校验FormSchema规则:确保测试输入符合校验要求(比如邮箱格式正确、姓名不为空等),可以通过查询页面上的错误提示元素排查:screen.queryByText(/请输入有效的邮箱/i)。
  • 异步处理:如果handleFormSubmit包含异步操作(比如接口请求),必须使用waitFor或async/await等待异步流程完成后再断言。

内容的提问来源于stack exchange,提问作者ReactDeveloper

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 09:15:33