使用Jest编写单元测试时如何mock Formik的useFormikContext钩子
最优实现方案汇总
这里提供3种可直接落地的方案,你可以根据项目场景选择:
方案1:用类型断言跳过不必要的属性校验(最快实现)
你的组件仅用到useFormikContext返回值中的getFieldMeta方法,完全不需要补齐所有20+属性,用TS类型断言绕开类型校验即可,代码量直接缩减90%:
import * as formik from 'formik'; import type { FormikContextType } from 'formik'; describe('AphInputLabel UnitTests', () => { const useFormikContextMock = jest.spyOn(formik, 'useFormikContext'); // 仅mock你实际用到的getFieldMeta方法 const mockGetFieldMeta = jest.fn((fieldName) => ({ // 按测试场景返回error值即可,其他getFieldMeta返回属性没用到可以不写 error: fieldName === 'testName' ? '字段错误提示' : undefined })); beforeEach(() => { useFormikContextMock.mockReturnValue({ getFieldMeta: mockGetFieldMeta // 其余所有没用到的属性直接用类型断言跳过校验 } as unknown as FormikContextType<any>); }); // 后续测试用例正常编写即可 });
优缺点:实现最快,适合组件依赖Formik属性少的场景;如果后续组件新增用到其他Formik属性,需要同步更新mock。
方案2:直接用Formik组件包裹被测组件(最推荐)
完全不需要mock钩子,直接模拟组件真实使用场景,用Formik原生组件包裹被测组件,既不需要处理类型问题,测试结果也更贴近线上运行情况,同时Formik内置的Field组件也不需要额外mock:
import { render, screen } from '@testing-library/react'; import { Formik } from 'formik'; import AphControlInput from './AphControlInput'; describe('AphInputLabel UnitTests', () => { it('字段报错时正确展示invalid样式', () => { render( <Formik initialValues={{ testName: '' }} initialErrors={{ testName: '该字段为必填项' }} initialTouched={{ testName: true }} onSubmit={() => {}} > <AphControlInput name="testName" label="测试输入框" /> </Formik> ); // 直接校验渲染结果即可 expect(screen.getByLabelText('测试输入框').parentElement).toHaveClass('aph-control-input--invalid'); }); });
优缺点:不需要维护任何mock逻辑,测试可靠性最高,后续组件新增依赖Formik属性也不用修改测试代码,是优先推荐的方案。
方案3:封装通用mock工厂(适合多组件复用场景)
如果你的项目里有大量组件依赖useFormikContext,可以提前封装一个通用的mock工厂函数,写一次之后所有组件测试都可以复用,每次只需要传需要自定义的属性即可:
// 单独抽离的通用mock工具 import type { FormikContextType } from 'formik'; export function createMockFormikContext(override: Partial<FormikContextType<any>> = {}): FormikContextType<any> { return { values: {}, errors: {}, touched: {}, isSubmitting: false, isValidating: false, submitCount: 0, getFieldMeta: jest.fn(() => ({ error: undefined, touched: false, value: '' })), setStatus: jest.fn(), setErrors: jest.fn(), setSubmitting: jest.fn(), setTouched: jest.fn(), setValues: jest.fn(), setFieldValue: jest.fn(), setFieldError: jest.fn(), setFieldTouched: jest.fn(), resetForm: jest.fn(), validateField: jest.fn(), setFormikState: jest.fn(), validateForm: jest.fn(() => Promise.resolve({})), submitForm: jest.fn(() => Promise.resolve()), handleSubmit: jest.fn(), handleReset: jest.fn(), ...override, } as FormikContextType<any>; } // 测试用例中使用 useFormikContextMock.mockReturnValue(createMockFormikContext({ getFieldMeta: jest.fn((name) => ({ error: '自定义错误提示' })) }));
优缺点:一次封装全项目复用,适合需要单独测试组件、不想引入Formik组件渲染的场景。
内容的提问来源于stack exchange,提问作者Vuk
相关产品推荐
相关产品推荐

