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

React Jest单元测试:无效表单仍触发onSubmit问题排查

问题:React表单原生验证在Jest测试中不生效

测试一个接收onSubmit函数的React提交表单组件,组件包含必填的email类型输入框。在浏览器中提交无效邮箱时,浏览器会自动阻止提交并显示错误提示;但在Jest测试中执行相同操作,mock的onSubmit函数仍会被调用,导致测试失败。

组件代码

interface AppProps {
  onSubmit: () => Promise<any>;
  onComplete: (data: any) => void;
}

function App(props: AppProps) {
  function handleSubmit(event: any) {
    event.preventDefault();

    props.onSubmit().then((response) => {
      props.onComplete(response);
    });
  }
  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="email"> Email </label>
      <input id="email" type="email" required />
      <button> Submit </button>
    </form>
  );
}

export default App;

测试代码

import { fireEvent, render, screen } from "@testing-library/react";
import App from "./App";

test("Invalid form isn't submitted", async () => {
  const onSubmit = jest.fn();
  onSubmit.mockResolvedValue("");

  const onComplete = jest.fn((value) => {
    console.log("Function called");
  });

  render(<App onSubmit={onSubmit} onComplete={onComplete} />);

  const input = screen.getByLabelText("Email");
  fireEvent.change(input, { target: { value: "invalid" } });

  const button = screen.getByText("Submit");  
  fireEvent.click(button);

  expect(onSubmit).not.toBeCalled();
});

测试失败输出

FAIL  src/App.test.tsx
● Invalid form isn't submitted
expect(jest.fn()).not.toBeCalled()
Expected number of calls: 0
Received number of calls: 1

解决方案

问题根源

fireEvent.click(button)是直接触发按钮的点击事件,不会触发浏览器原生的表单验证流程,所以会直接调用form的onSubmit回调。而真实浏览器中点击提交按钮时,会先执行表单验证,验证不通过则不会触发onSubmit。

两种解决方式

  1. 使用userEvent模拟真实用户交互
    @testing-library/user-event会模拟更贴近真实用户的操作,包括触发表单验证。需要先安装依赖:
npm install @testing-library/user-event --save-dev

修改后的测试代码:

import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import App from "./App";

test("Invalid form isn't submitted", async () => {
  const user = userEvent.setup();
  const onSubmit = jest.fn();
  onSubmit.mockResolvedValue("");

  const onComplete = jest.fn();

  render(<App onSubmit={onSubmit} onComplete={onComplete} />);

  const input = screen.getByLabelText("Email");
  await user.type(input, "invalid");

  const button = screen.getByText("Submit");  
  await user.click(button);

  expect(onSubmit).not.toBeCalled();
});
  1. 在组件中手动验证表单
    在handleSubmit中主动调用表单的checkValidity()方法,验证不通过则不执行后续逻辑:
    修改后的组件代码:
interface AppProps {
  onSubmit: () => Promise<any>;
  onComplete: (data: any) => void;
}

function App(props: AppProps) {
  function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault();
    const form = event.currentTarget;
    // 手动检查表单有效性
    if (!form.checkValidity()) {
      // 触发浏览器的错误提示(可选)
      form.reportValidity();
      return;
    }

    props.onSubmit().then((response) => {
      props.onComplete(response);
    });
  }
  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="email"> Email </label>
      <input id="email" type="email" required />
      <button> Submit </button>
    </form>
  );
}

export default App;

这种方式不管是浏览器环境还是测试环境,都会先验证表单有效性,再决定是否调用onSubmit。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 11:32:50