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

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 : &#41;</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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 22:35:20