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

React测试ContactForm组件传onSubmit属性遇TypeScript类型错误

React Testing Library测试react-hook-form组件时的TypeScript错误

错误信息

Type '{ onSubmit: Mock<any, any>; }' is not assignable to type 'IntrinsicAttributes'.
Property 'onSubmit' does not exist on type 'IntrinsicAttributes'.

相关代码

测试代码

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

  const renderForm = () => {
    const Wrapper = (props: { children: React.ReactNode }) => {
      const formMethods = useForm();
      return <FormProvider {...formMethods}>{props.children}</FormProvider>;
    };
    customRender(
      <Wrapper>
        <ContactForm onSubmit={onSubmit} /> // 错误发生在这里
      </Wrapper>,
      {
        withRouter: true,
      }
    );
  };

  it("successful submit", () => {
    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 });

    expect(emailInput).toBeInTheDocument();
    expect(fNameInput).toBeInTheDocument();


    fireEvent.input(emailInput, "abc@abc.com");
    fireEvent.input(fNameInput, "Alex");
    fireEvent.click(submitButton);

    expect(onSubmit).toHaveBeenCalledTimes(1);
  });
});

组件代码

export function ContactForm() {

  const methods = useForm<FormSchemaType>({
    resolver: zodResolver(FormSchema),
  });
  const { handleSubmit, reset } = methods;
  const onSubmit: SubmitHandler<FormSchemaType> = data => {
    // 内部提交逻辑
  };

  return (
    <>
      <FormProvider {...methods}>
          <FormWrapper>
            <form onSubmit={handleSubmit(onSubmit)}>
              {/* 表单输入项 */}
            </form>
          </FormWrapper>
      </FormProvider>
    </>
  );
}

问题原因

ContactForm组件没有定义onSubmit属性,它内部自行实现了提交逻辑,所以测试代码中尝试给组件传递onSubmit props时,TypeScript会报错,且组件也不会接收并使用这个外部传入的函数。另外测试代码中的Wrapper是多余的——组件内部已经通过FormProvider提供了form实例,外层再包一个FormProvider可能导致form方法冲突。

解决方案

方案一:修改组件,支持外部传入onSubmit

调整组件定义,让它接收外部传入的提交函数,这样测试时可以传入mock函数验证提交行为:

import { useForm, SubmitHandler, FormProvider } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { FormSchema, FormSchemaType } from "./your-schema-path";

// 定义组件Props类型
type ContactFormProps = {
  onSubmit: SubmitHandler<FormSchemaType>;
};

export function ContactForm({ onSubmit }: ContactFormProps) {
  const methods = useForm<FormSchemaType>({
    resolver: zodResolver(FormSchema),
  });
  const { handleSubmit, reset } = methods;

  return (
    <>
      <FormProvider {...methods}>
        <FormWrapper>
          <form onSubmit={handleSubmit(onSubmit)}>
            {/* 表单输入项 */}
          </form>
        </FormWrapper>
      </FormProvider>
    </>
  );
}

修改后的测试代码(移除多余Wrapper,修正fireEvent用法):

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

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

  it("triggers onSubmit with correct data on submission", () => {
    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的传参方式
    fireEvent.input(emailInput, { target: { value: "abc@abc.com" } });
    fireEvent.input(fNameInput, { target: { value: "Alex" } });
    fireEvent.click(submitButton);

    expect(onSubmit).toHaveBeenCalledTimes(1);
    // 可进一步断言提交的表单数据是否正确
    expect(onSubmit).toHaveBeenCalledWith({
      email: "abc@abc.com",
      fname: "Alex"
      // 补充其他表单字段
    });
  });
});

方案二:不修改组件,验证内部提交逻辑的副作用

如果不想修改组件,可以mock内部提交逻辑中的依赖(比如API调用),通过断言依赖是否被调用来验证提交行为。

例如,如果组件内部的onSubmit调用了一个sendContactData函数,测试时可以这样做:

// 先mock依赖
jest.mock("./api", () => ({
  sendContactData: jest.fn().mockResolvedValue({ success: true })
}));

import { sendContactData } from "./api";

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

  it("calls sendContactData with correct data on submission", 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);

    expect(sendContactData).toHaveBeenCalledTimes(1);
    expect(sendContactData).toHaveBeenCalledWith({
      email: "abc@abc.com",
      fname: "Alex"
    });
  });
});

内容的提问来源于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 21:05:16