使用 jest 编写单元测试时如何模拟 formik useFormikContext 挂钩
How to mock the formik useFormikContext hook when writing unit tests with jest
我有一个简单的组件(见下文),它主要尝试使用 useFormikContext 挂钩从 formik FormContext 中获取一些数据。
但是,当尝试为此组件编写单元测试时,它要我模拟钩子,这很好,但是,使用打字稿模拟钩子意味着返回超过 20 个属性,其中大部分是各种方法和函数。
有没有人找到更好的方法?即使我让它工作似乎有点烦人,因为我只需要 1 个字段。
组件
const AphControlInput: React.FC<IAphControlInput> = ({ name, value, label, type, small, disabled, vertical = true, className = '', ...attributeOptions }) => {
const form = useFormikContext();
return (
<>
<div className={
`aph-control-input ${className}` +
`${small ? ' aph-control-input--small' : ''}` +
`${vertical ? ' aph-control-input--block' : ''}` +
`${form.getFieldMeta(name).error ? ' aph-control-input--invalid' : ''}`
}
>
<Field
className='aph-control-input__input'
name={name}
id={value ? value?.toString() : name}
type={type}
value={value ? value : undefined}
disabled={disabled}
{...attributeOptions}
/>
<label className='aph-control-input__text' htmlFor={value ? value?.toString() : name}>
{label}
</label>
</div>
</>
);
};
单元测试(不完整这只是一个快速尝试模拟所有 returns 的钩子)
describe('AphInputLabel UnitTests', () => {
let wrapper: any;
const useFormikContextMock = jest.spyOn(formik, 'useFormikContext');
beforeEach(() => {
useFormikContextMock.mockReturnValue({
values: { testName: 'testValue' },
getFieldMeta: getFieldMetaMock,
touched: true,
isSubmitting: false,
isValidating: false,
errors: false,
submitCount: 0,
setStatus: (status?: any) => { return null },
setErrors: (errors?: FormikErrors<any>) => { return null },
setSubmitting: (isSubmitting: boolean) => { return null },
setTouched: (touched: FormikTouched<any>, shouldValidate?: boolean) => { return null },
setValues: (values: React.SetStateAction<any>, shouldValidate?: boolean) => { return null },
setFieldValue: (field: string, value: any, shouldValidate?: boolean) => { return null },
setFieldError: (field: string, message: string | undefined) => { return null },
setFieldTouched: (field: string, isTouched?: boolean, shouldValidate?: boolean) => { return null },
resetForm: (nextState?: Partial<FormikState<any>>) => { return null },
validateField: (field: string) => { return null },
setFormikState: (f: FormikState<any> | ((prevState: FormikState<any>) => FormikState<any>), cb?: () => void) => null,
validateForm: (values?: any) => { return new Promise<FormikErrors<unknown>>((resolve, reject) => {
const formikErrors: FormikErrors<any> = {
'testName': ''
}
return formikErrors;
});
},
submitForm: () => new Promise<void>(() => null),
handleSubmit: (e?: React.FormEvent<HTMLFormElement> | undefined) => null,
handleReset: (e?: React.SyntheticEvent<any>) => null,
});
}
}
我解决了这个问题,并不是 100% 确定它是最好的解决方案,但已在此处发布,以防它能帮助遇到类似问题的任何人。
我基本上重写了 FormikType 允许我忽略我没有使用的所有字段和方法,它显然有一些缺点,因为它正在删除类型安全,但我认为因为它只在单元内部测试一下还是可以的。
导入
import * as Formik from 'formik';
测试设置
describe('AphControlInput: UnitTests', () => {
let wrapper: any;
const useFormikContextMock = jest.spyOn(Formik, 'useFormikContext');
beforeEach(() => {
useFormikContextMock.mockReturnValue({
getFieldMeta: getFieldMetaMock
} as unknown as any);
wrapper = shallow(
<AphTextInput {...baseComponentProps} />
);
});
}
辅助方法
export const getFieldMetaMock = (name: string) => {
return ({
value: 'testValue',
initialTouched: true,
touched: false,
});
};
我有一个简单的组件(见下文),它主要尝试使用 useFormikContext 挂钩从 formik FormContext 中获取一些数据。
但是,当尝试为此组件编写单元测试时,它要我模拟钩子,这很好,但是,使用打字稿模拟钩子意味着返回超过 20 个属性,其中大部分是各种方法和函数。
有没有人找到更好的方法?即使我让它工作似乎有点烦人,因为我只需要 1 个字段。
组件
const AphControlInput: React.FC<IAphControlInput> = ({ name, value, label, type, small, disabled, vertical = true, className = '', ...attributeOptions }) => {
const form = useFormikContext();
return (
<>
<div className={
`aph-control-input ${className}` +
`${small ? ' aph-control-input--small' : ''}` +
`${vertical ? ' aph-control-input--block' : ''}` +
`${form.getFieldMeta(name).error ? ' aph-control-input--invalid' : ''}`
}
>
<Field
className='aph-control-input__input'
name={name}
id={value ? value?.toString() : name}
type={type}
value={value ? value : undefined}
disabled={disabled}
{...attributeOptions}
/>
<label className='aph-control-input__text' htmlFor={value ? value?.toString() : name}>
{label}
</label>
</div>
</>
);
};
单元测试(不完整这只是一个快速尝试模拟所有 returns 的钩子)
describe('AphInputLabel UnitTests', () => {
let wrapper: any;
const useFormikContextMock = jest.spyOn(formik, 'useFormikContext');
beforeEach(() => {
useFormikContextMock.mockReturnValue({
values: { testName: 'testValue' },
getFieldMeta: getFieldMetaMock,
touched: true,
isSubmitting: false,
isValidating: false,
errors: false,
submitCount: 0,
setStatus: (status?: any) => { return null },
setErrors: (errors?: FormikErrors<any>) => { return null },
setSubmitting: (isSubmitting: boolean) => { return null },
setTouched: (touched: FormikTouched<any>, shouldValidate?: boolean) => { return null },
setValues: (values: React.SetStateAction<any>, shouldValidate?: boolean) => { return null },
setFieldValue: (field: string, value: any, shouldValidate?: boolean) => { return null },
setFieldError: (field: string, message: string | undefined) => { return null },
setFieldTouched: (field: string, isTouched?: boolean, shouldValidate?: boolean) => { return null },
resetForm: (nextState?: Partial<FormikState<any>>) => { return null },
validateField: (field: string) => { return null },
setFormikState: (f: FormikState<any> | ((prevState: FormikState<any>) => FormikState<any>), cb?: () => void) => null,
validateForm: (values?: any) => { return new Promise<FormikErrors<unknown>>((resolve, reject) => {
const formikErrors: FormikErrors<any> = {
'testName': ''
}
return formikErrors;
});
},
submitForm: () => new Promise<void>(() => null),
handleSubmit: (e?: React.FormEvent<HTMLFormElement> | undefined) => null,
handleReset: (e?: React.SyntheticEvent<any>) => null,
});
}
}
我解决了这个问题,并不是 100% 确定它是最好的解决方案,但已在此处发布,以防它能帮助遇到类似问题的任何人。
我基本上重写了 FormikType 允许我忽略我没有使用的所有字段和方法,它显然有一些缺点,因为它正在删除类型安全,但我认为因为它只在单元内部测试一下还是可以的。
导入
import * as Formik from 'formik';
测试设置
describe('AphControlInput: UnitTests', () => {
let wrapper: any;
const useFormikContextMock = jest.spyOn(Formik, 'useFormikContext');
beforeEach(() => {
useFormikContextMock.mockReturnValue({
getFieldMeta: getFieldMetaMock
} as unknown as any);
wrapper = shallow(
<AphTextInput {...baseComponentProps} />
);
});
}
辅助方法
export const getFieldMetaMock = (name: string) => {
return ({
value: 'testValue',
initialTouched: true,
touched: false,
});
};