Reputation: 133
TL;DR
How to write unit test with Jest & Enzyme for components with 'useField' hook? On shallow render I get this error
Warning: Formik context is undefined, please verify you are calling
useFormikContext() as child of a <Formik> component
TypeError: Cannot read property 'getFieldProps' of undefined
Details
The project build with
It's a learning project, so I am playing around with different approaches. That's why I've put all components in different files thought it might not be necessary.
Structure:
Formik.tsx
|
| AddContactForm.tsx
|
| TextInput.tsx
| TextInput.test.tsx
Details:
Formik.tsx Just a wrapper where we have all properties of the form
<Formik
initialValues={initialValues}
validationSchema={...}
onSubmit={...};
component={AddContactForm}
/>
AddContactForm.tsx Here I am passing field meta and props to input. It seems to be not the best solution, I'd like to use useField() hook inside a component itself
<Form>
<TextInput
label="First Name"
name={"firstName"}
placeholder="Jane"
field={getFieldProps("firstName")}
meta={getFieldMeta("firstName")}
/>
<button type="submit">Submit</button>
</Form>
TextInput.tsx This is current solution - I can write unit tests for it - for example snapshot testing.
const TextInput: React.FC<MyInput> = React.memo(
({ label, field, meta}: MyInput) => {
return (
<>
<TextField
label={label}
type="text"
{...field}
error={meta?.touched && meta?.error ? true : undefined}
helperText={meta?.touched ? meta?.error : undefined}
/>
</>
);
}
);
TextInput.test.tsx Here I have to write big mockProps object to mock all things :(
describe("<TextInput/>", () => {
it("Match Snapshot", () => {
const mockProps: MyInput = {
label: "label",
name: "name",
placeholder: "placeholder",
meta: {
touched: false,
error: "",
initialError: "",
initialTouched: false,
initialValue: "",
value: "",
},
field: {
value: "",
checked: false,
onChange: jest.fn(),
onBlur: jest.fn(),
multiple: undefined,
name: "firstName",
},
};
expect(
shallow(
<TextInput
label="label"
name="name"
placeholder="placeholder"
{...mockProps.meta}
{...mockProps.field}
/>
)
).toMatchSnapshot();
});
});
Instead, what I want is to get field and meta not by props, but with useField() hook.
TextField.tsx
const TextInput: React.FC<MyInput> = React.memo(
({ label, ...props }: MyInput) => {
const [field, meta] = useField(props);
return (
<>
<TextField
label={label}
type="text"
{...field}
{...props}
error={meta?.touched && meta?.error ? true : undefined}
helperText={meta?.touched ? meta?.error : undefined}
/>
</>
);
}
);
But then I have not clue how to write a test for it. It seems like it wants to have Formik context inside a test, but it's not possible to use useFormikContext() hook in test file as it violates rules of hooks usage.
Upvotes: 13
Views: 12568
Reputation: 4565
To learn more about Mocking in Jest, you should read the official documentation on:
You can also look at Enzyme's ShallowWrapper
API documentation
// TextInput.test.tsx
import React from 'react';
import { useField } from 'formik'; // package will be auto mocked
import TextInput from '...';
jest.mock('formik'); // formik package is auto mocked
describe("<TextInput/>", () => {
it("Match Snapshot", () => {
const mockMeta = {
touched: false,
error: "",
initialError: "",
initialTouched: false,
initialValue: "",
value: "",
}
const mockField = {
value: "",
checked: false,
onChange: jest.fn(),
onBlur: jest.fn(),
multiple: undefined,
name: "firstName",
};
useField.mockReturnValue([mockField, mockMeta]);
const mockProps = {...};
expect(
shallow(<TextInput {...mockProps} />).debug()
).toMatchSnapshot();
});
});
// TextInput.tsx
import React from 'react';
import { useField } from 'formik';
import ...
const TextInput: React.FC<MyInput> = React.memo(
({ label, ...props }: MyInput) => {
const [field, meta] = useField(props);
return (
<>
<TextField
label={label}
type="text"
{...field}
{...props}
error={meta?.touched && meta?.error ? true : undefined}
helperText={meta?.touched ? meta?.error : undefined}
/>
</>
);
}
);
Upvotes: 6