Jest测试疑问:为何组件内handleSubmit未被检测到调用?
解决Formik组件Jest测试中handleSubmit断言失败的问题
问题场景
- 参考Testing Library的Formik示例实现了
MyForm组件,组件内部定义了异步handleSubmit函数,表单提交时会打印提交值 - 测试代码中创建了
jest.fn()的handleSubmit,但未与组件关联,导致断言该函数被调用1次失败,尽管控制台已正常输出{ firstName: 'John' }
问题根源
测试里的mock函数没有替换组件内部的handleSubmit逻辑,组件依然调用自身定义的提交函数,所以测试中的mock永远不会被触发,断言自然失败。
解决方案
方案1:修改组件,支持传入外部handleSubmit
让组件接受可选的handleSubmit props,优先使用外部传入的函数,否则用内部默认实现:
import { Formik, Form, Field } from 'formik'; const MyForm = ({ handleSubmit: externalSubmit }) => { const internalSubmit = async (values) => { console.log(values); }; // 优先使用外部传入的提交函数 const submitHandler = externalSubmit || internalSubmit; return ( <Formik initialValues={{ firstName: '' }} onSubmit={submitHandler}> <Form> <Field name="firstName" placeholder="请输入名字" /> <button type="submit">提交</button> </Form> </Formik> ); }; export default MyForm;
对应的测试代码:
import { render, screen, fireEvent } from '@testing-library/react'; import MyForm from './MyForm'; test('表单提交时触发handleSubmit', async () => { const mockSubmit = jest.fn(); // 把mock函数传入组件 render(<MyForm handleSubmit={mockSubmit} />); // 输入表单值 fireEvent.change(screen.getByPlaceholderText('请输入名字'), { target: { value: 'John' } }); // 点击提交按钮 fireEvent.click(screen.getByText('提交')); // 断言mock函数被调用1次,且参数正确 expect(mockSubmit).toHaveBeenCalledTimes(1); expect(mockSubmit).toHaveBeenCalledWith({ firstName: 'John' }); });
方案2:不修改组件,用Jest mock替换内部函数
如果无法修改组件代码,可以通过jest.mock直接mock组件内部的提交逻辑:
import { render, screen, fireEvent } from '@testing-library/react'; import MyForm from './MyForm'; // Mock组件,替换内部的handleSubmit jest.mock('./MyForm', () => { const OriginalMyForm = jest.requireActual('./MyForm').default; return { default: ({ handleSubmit }) => { return <OriginalMyForm handleSubmit={handleSubmit} />; } }; }); test('表单提交时触发handleSubmit', async () => { const mockSubmit = jest.fn(); render(<MyForm handleSubmit={mockSubmit} />); fireEvent.change(screen.getByRole('textbox'), { target: { value: 'John' } }); fireEvent.click(screen.getByText('提交')); expect(mockSubmit).toHaveBeenCalledTimes(1); expect(mockSubmit).toHaveBeenCalledWith({ firstName: 'John' }); });
内容的提问来源于stack exchange,提问作者Joel
相关产品推荐
相关产品推荐

