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

Vitest+React Testing Library测试时,表单提交Mock函数未触发问题

问题

我正尝试为一个接收handleSubmit作为props的React组件编写测试,该组件会在表单提交的回调中调用此handleSubmit。我使用了vitest和react-testing-library,组件代码如下:

const APIKeyInput = ({ handleSubmit }: APIKeyInputProps) => {
  const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    handleSubmit(event.currentTarget.elements.apiKey.value);
  };
  return (
    <form onSubmit={onSubmit}>
      <label htmlFor="apiKey">Insira sua chave para a API:</label>
      <input type="text" name="apiKey" />
      <input type="submit" value="Entrar" />
    </form>
  );
};

在测试中,我用vi.fn()创建了一个Mock函数,并将其作为handleSubmit props传入,测试代码如下:

import { describe, it, expect, vi } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import APIKeyInput from "./App";

describe("APIKeyInput", () => {
  it("calls onSubmit when submitted", async () => {
    const user = userEvent.setup();
    const onSubmitMock = vi.fn();
    render(<APIKeyInput handleSubmit={onSubmitMock} />);

    expect(onSubmitMock).not.toHaveBeenCalled();

    const apiSubmit = screen.getByRole("button");

    await user.click(apiSubmit);

    await waitFor(() => {
      expect(onSubmitMock).toHaveBeenCalledTimes(1);
    });
  });
});

通过日志可以看到,当触发user.click(apiSubmit)时,组件内的onSubmit函数已被调用,但onSubmitMock却未被触发,请问我遗漏了什么?

解决方案

核心问题是你直接通过event.currentTarget.elements.apiKey.value获取输入值的方式存在隐患——当测试中点击提交按钮时,这个DOM元素的引用可能无法被正确解析,导致代码在调用handleSubmit前就静默出错了(错误没冒泡到测试层面,所以你只看到组件内的onSubmit执行了,但handleSubmitMock没触发)。

可以试试这两种修复方式:

方式一:改成受控组件(推荐)

用React state管理输入框的值,彻底避免直接操作DOM,这也是React的标准写法:

import { useState } from 'react';

const APIKeyInput = ({ handleSubmit }: APIKeyInputProps) => {
  const [apiKey, setApiKey] = useState('');
  
  const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    handleSubmit(apiKey);
  };
  
  return (
    <form onSubmit={onSubmit}>
      <label htmlFor="apiKey">Insira sua chave para a API:</label>
      <input 
        type="text" 
        name="apiKey" 
        value={apiKey}
        onChange={(e) => setApiKey(e.target.value)}
      />
      <input type="submit" value="Entrar" />
    </form>
  );
};

这种写法不仅让测试更稳定,也符合React的数据流设计。

方式二:调整测试逻辑(不修改组件的前提下)

如果不想改组件,需要确保测试中先填入输入值,并且改用user.submit()直接提交表单(比点击按钮更可靠):

it("calls onSubmit when submitted", async () => {
  const user = userEvent.setup();
  const onSubmitMock = vi.fn();
  render(<APIKeyInput handleSubmit={onSubmitMock} />);

  expect(onSubmitMock).not.toHaveBeenCalled();

  // 先找到输入框并填入测试值
  const apiInput = screen.getByLabelText(/Insira sua chave para a API:/i);
  await user.type(apiInput, 'test-api-key');
  
  // 直接提交表单
  const form = screen.getByRole('form');
  await user.submit(form);

  // 不需要waitFor,user.submit是异步操作,等待完成后直接断言
  expect(onSubmitMock).toHaveBeenCalledTimes(1);
  expect(onSubmitMock).toHaveBeenCalledWith('test-api-key');
});

内容的提问来源于stack exchange,提问作者Felipe Martins

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 08:42:49