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
相关产品推荐
相关产品推荐

