React表单Jest测试:onSubmit函数未被调用问题求助
问题分析与解决方案
你的测试代码存在核心逻辑错误:你定义的onSubmit jest mock从未被组件调用。组件的表单onSubmit绑定的是内部私有函数sendEmail,和测试里声明的onSubmit变量完全没有关联,所以无论表单是否提交,这个mock的调用次数永远是0。
解决方案1:重构组件,将提交逻辑作为props传入(推荐)
这种方式能提升组件的可测试性和复用性,把业务逻辑从UI组件中解耦出来。
修改组件代码:
// ContactForm.tsx const ContactForm = ({ onSubmit }: { onSubmit: (data: {name: string; email: string; message: string}) => void }) => { const [messageInput, setMessageInput] = useState(""); const [isThanksRendered, setThanksRendered] = useState(false); const pageYOffset = useWindowYOffset(); const screenWidth = useWindowWidth(); const textAreaRef = useRef<HTMLTextAreaElement>(null); const form = useRef<HTMLFormElement>(null as any); useAutosizeTextArea(textAreaRef.current, messageInput); const handleSubmit = (event: any) => { event.preventDefault(); setThanksRendered(true); const emailData = { name: form.current.user_name.value, email: form.current.user_email.value, message: form.current.message.value, } // 调用传入的onSubmit prop onSubmit(emailData); // 原有的emailjs发送逻辑可以保留,或者也作为props传入 emailjs.send('service_mqqjzhj', 'template_yascyt3', emailData, 'vxsbnr4XFI-SFNKvG') .then((result: any) => { console.log(result.text); }, (error: any) => { console.log(error.text); } ); }; // 其余JSX代码保持不变,仅把form的onSubmit改为handleSubmit return ( <> {isThanksRendered ? ( <ThanksContainer> <Thanks>Thank you for reaching out. I will get back to you as soon as possible : )</Thanks> </ThanksContainer> ) : ( <FormContainer textAreaHeight={textAreaRef?.current?.offsetHeight} ref={form} onSubmit={handleSubmit} // 这里改为handleSubmit pageYOffset={pageYOffset} screenWidth={screenWidth} data-testid='form-submit' > {/* 表单内容保持不变 */} </FormContainer> )} </> ) } export default ContactForm
修改测试代码:
// ContactForm.test.tsx import React from "react"; import "@testing-library/jest-dom/extend-expect"; import { screen, render } from "@testing-library/react"; import ContactForm from '../ContactForm' import user from '@testing-library/user-event'; describe("ContactForm", () => { it("onSubmit is called when all fields are filled and pass validations", async () => { // 定义mock的onSubmit函数 const mockOnSubmit = jest.fn(); // 渲染组件时传入mock函数 render(<ContactForm onSubmit={mockOnSubmit} />) const fullName = screen.getByTestId('form-name'); await user.type(fullName, 'Full Name') const email = screen.getByTestId('form-email'); await user.type(email, 'example@email.com') const message = screen.getByTestId('form-message'); await user.type(message, 'random message with more than 30 characters due to requirements') const button = screen.getByTestId('form-button'); await user.click(button); // 断言mock函数被调用1次 expect(mockOnSubmit).toHaveBeenCalledTimes(1); // 断言调用参数正确 expect(mockOnSubmit).toHaveBeenCalledWith({ name: "Full Name", email: "example@email.com", message: "random message with more than 30 characters due to requirements" }); }); });
解决方案2:不重构组件,mock emailjs.send验证调用
如果你不想修改组件结构,可以mockemailjs的send方法,通过验证它的调用来间接确认表单提交逻辑正常执行:
// ContactForm.test.tsx import React from "react"; import "@testing-library/jest-dom/extend-expect"; import { screen, render } from "@testing-library/react"; import ContactForm from '../ContactForm' import user from '@testing-library/user-event'; // 引入emailjs并mock import * as emailjs from '@emailjs/browser'; jest.mock('@emailjs/browser'); describe("ContactForm", () => { beforeEach(() => { // 每次测试前清空mock (emailjs.send as jest.Mock).mockClear(); render(<ContactForm />) }) it("triggers email sending when form is submitted", async () => { const fullName = screen.getByTestId('form-name'); await user.type(fullName, 'Full Name') const email = screen.getByTestId('form-email'); await user.type(email, 'example@email.com') const message = screen.getByTestId('form-message'); await user.type(message, 'random message with more than 30 characters due to requirements') const button = screen.getByTestId('form-button'); await user.click(button); // 验证emailjs.send被调用,且参数正确 expect(emailjs.send).toHaveBeenCalledWith( 'service_mqqjzhj', 'template_yascyt3', { name: "Full Name", email: "example@email.com", message: "random message with more than 30 characters due to requirements" }, 'vxsbnr4XFI-SFNKvG' ); // 同时验证UI变化:感谢信息是否显示 expect(screen.getByText('Thank you for reaching out. I will get back to you as soon as possible :)')).toBeInTheDocument(); }); });
解决方案3:测试用户可见的行为(最贴近真实场景)
测试的核心是验证用户能感知到的结果,而非内部函数调用。你可以直接测试提交表单后是否显示感谢文案:
// ContactForm.test.tsx import React from "react"; import "@testing-library/jest-dom/extend-expect"; import { screen, render } from "@testing-library/react"; import ContactForm from '../ContactForm' import user from '@testing-library/user-event'; describe("ContactForm", () => { beforeEach(() => { render(<ContactForm />) }) it("shows thank you message after valid form submission", async () => { // 填充表单 const fullName = screen.getByTestId('form-name'); await user.type(fullName, 'Full Name') const email = screen.getByTestId('form-email'); await user.type(email, 'example@email.com') const message = screen.getByTestId('form-message'); await user.type(message, 'random message with more than 30 characters due to requirements') // 点击提交 const button = screen.getByTestId('form-button'); await user.click(button); // 验证感谢文案出现 expect(screen.getByText('Thank you for reaching out. I will get back to you as soon as possible :)')).toBeInTheDocument(); // 验证表单不再显示 expect(screen.queryByTestId('form-submit')).not.toBeInTheDocument(); }); });
内容的提问来源于stack exchange,提问作者Luis Couto
相关产品推荐
相关产品推荐

