Reputation: 873
I have a simple component see below, that basically attempts to grab some data from the formik FormContext using the useFormikContext hook.
However when attempting to write unit tests for this component it wants me to mock the hook which is fine, however, mocking the hook with typescript means returning well over 20 properties most of which are a variety of methods and functions.
Has anyone found a better way of doing this? Just seems a bit annoying even if I get it to work as I only need 1 field from the hook.
Component
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>
</>
);
};
Unit test (Incomplete this was just a quick attempt at mocking all the returns for the hook)
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,
});
}
}
Upvotes: 8
Views: 15988
Reputation: 2087
Also, you can mock the Formik library & its desired functions, in this way:
jest.mock('formik', () => ({
useFormikContext: jest.fn().mockReturnValue({
getFieldMeta: jest.fn(),
}),
}));
Just put this code at the top of your unit test file, outside of the describe
scope.
Upvotes: 2
Reputation: 873
I resolved this issue not 100% sure it is the best solution but have posted here in case it helps anyone with a similar issue.
I basically overwrote the FormikType allowing me to ignore all of the fields and methods I wasn't using, it clearly has some drawbacks as it is removing the type-safety, but I figured since it was only inside the unit test it is somewhat okay.
Import
import * as Formik from 'formik';
Test setup
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} />
);
});
}
Helper method
export const getFieldMetaMock = (name: string) => {
return ({
value: 'testValue',
initialTouched: true,
touched: false,
});
};
Upvotes: 7