单元测试无按钮时如何提交Formik表单?fireEvent.submit失效
解决Formik表单在React单元测试中无法提交的问题
在测试Formik表单时,调用fireEvent.submit(form)无法触发表单提交,测试报错显示传入的handleSubmit未被调用。
问题原因
- 表单验证拦截:Formik的
validate函数要求coords字段必须有值,但测试中仅手动输入了地址文本,未触发地址选择逻辑设置coords;同时游戏字段仅输入文本,未完成下拉选择流程,导致验证失败,Formik直接拦截了提交动作。 - 事件模拟不贴合真实场景:
fireEvent.submit仅触发DOM层面的submit事件,而Formik的提交逻辑依赖更完整的用户交互流程(比如字段的touched状态、内部状态同步),userEvent工具能更精准模拟真实用户操作。
解决步骤
- 确保表单验证通过:
- 模拟用户选择地址下拉项的行为,触发
coords字段的设置; - 模拟用户选择游戏下拉项,确保
game字段符合验证要求; - 确保所有必填字段的
touched状态为true(Formik默认在字段失焦后标记为touched,可通过模拟失焦或选择下拉项自动触发)。
- 模拟用户选择地址下拉项的行为,触发
- 使用
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
相关产品推荐
相关产品推荐

