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

单元测试无按钮时如何提交Formik表单?fireEvent.submit失效

解决Formik表单在React单元测试中无法提交的问题

在测试Formik表单时,调用fireEvent.submit(form)无法触发表单提交,测试报错显示传入的handleSubmit未被调用。

问题原因

  1. 表单验证拦截:Formik的validate函数要求coords字段必须有值,但测试中仅手动输入了地址文本,未触发地址选择逻辑设置coords;同时游戏字段仅输入文本,未完成下拉选择流程,导致验证失败,Formik直接拦截了提交动作。
  2. 事件模拟不贴合真实场景:fireEvent.submit仅触发DOM层面的submit事件,而Formik的提交逻辑依赖更完整的用户交互流程(比如字段的touched状态、内部状态同步),userEvent工具能更精准模拟真实用户操作。

解决步骤

  1. 确保表单验证通过:
    • 模拟用户选择地址下拉项的行为,触发coords字段的设置;
    • 模拟用户选择游戏下拉项,确保game字段符合验证要求;
    • 确保所有必填字段的touched状态为true(Formik默认在字段失焦后标记为touched,可通过模拟失焦或选择下拉项自动触发)。
  2. 使用userEvent.submit替代fireEvent.submit:userEvent的submit方法会完整模拟用户提交表单的流程,包括触发Formik内部的状态校验与提交逻辑。

修改后的测试代码示例

import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import BasicInfo from '.';

// Mock地图钩子,返回预设地址数据
jest.mock('../../hooks/useMapbox', () => ({
  __esModule: true,
  default: () => ({
    data: [{ place_name: '21 2nd St.', center: [-74.006, 40.7128] }],
    forward: jest.fn(),
    getStaticMapUrl: jest.fn(() => 'mock-static-map-url'),
  }),
}));

// Mock游戏钩子,返回预设游戏数据
jest.mock('../../hooks/useGames', () => ({
  __esModule: true,
  default: () => ({
    data: [{ name: 'Scrabble' }],
    search: jest.fn(),
    leanGame: (game) => game,
  }),
}));

describe('BasicInfo', () => {
  it('can submit form successfully', async () => {
    const handleSubmit = jest.fn();
    render(<BasicInfo onSubmit={handleSubmit} />);
    const user = userEvent.setup();

    // 填写活动标题
    await user.type(screen.getByLabelText('Event title'), 'Event title 1');
    // 搜索并选择游戏
    await user.type(screen.getByLabelText('Featured game'), 'Scrabble');
    await waitFor(() => screen.getByText('Scrabble'));
    await user.click(screen.getByText('Scrabble'));
    // 搜索并选择地址
    await user.type(screen.getByLabelText('Venue location'), '21 2nd St.');
    await waitFor(() => screen.getByText('21 2nd St.'));
    await user.click(screen.getByText('21 2nd St.'));
    // 填写活动时间
    await user.type(screen.getByLabelText('Event starts'), '2017-06-01T08:30');
    await user.type(screen.getByLabelText('Event ends'), '2017-06-01T08:30');

    // 使用userEvent提交表单
    const form = screen.getByTestId<HTMLFormElement>('basic-info');
    await user.submit(form);

    await waitFor(() => {
      expect(handleSubmit).toHaveBeenCalledWith({
        title: 'Event title 1',
        game: { name: 'Scrabble' },
        address: '21 2nd St.',
        starts_at: '2017-06-01T08:30',
        ends_at: '2017-06-01T08:30',
        coords: JSON.stringify([-74.006, 40.7128]),
      });
    });
  });
});

额外提示

如果表单中有明确的提交按钮,也可以直接模拟点击按钮的行为(await user.click(screen.getByRole('button', { name: /submit/i }))),这种方式更贴近真实用户操作流程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 09:09:23