使用 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,
  });
};