javascriptreactjsunit-testingreact-final-formfinal-form

How to unit test react final form's Field Components?


I am writing a form component using react-final-form library. As component starts growing, I split it into multiple sub components such that parent component has the <Form>{... }</Form> component and related <Field>{... }</Field> are in separate components. Now, I'm trying to write unit test for my separated sub components containing <Field></Field>, I'm getting the error as <Field>{... }</Field> have dependency on <Form>{... }</Form> and cannot run separately. Below is the exact error message which shows up when I try to run unit test.

useField must be used inside of a component

One way I can solve this is to mock my actual <Field>{... }</Field> component in its unit test, but I feel that won't be a correct approach as for unit test I'm mocking the actual component which needs to get tested.

Could someone please help me what way should I go for ? Is it the right way to mock the component in its own unit test and then test it or should I have to think of restructuring my parent <Form> and its sub components ? And how can I restructure it better, any ideas?


Solution

  • Field component uses useField hook, useField hook uses useForm underly. useForm uses React.useContext(ReactFinalFormContext) underly. As you can see, it uses ReactFinalFormContext, so the component which uses this react context must be a descendant of ReactFinalFormContext.Provider. ReactFinalFormContext.Provider is provided by Form component.

    I recommend you to use React Testing Library to test your react components.

    It's not recommended to mock the component, you should test your components

    in the way a user would use them. Users don't care what happens behind the scenes, they just see and interact with the output.

    For more, see Why should I use React Testing Library

    You should wrap your Field custom component with a Form component. See official test recipes

    E.g.

      it("should render via children render function", () => {
        const { getByTestId } = render(
          <Form onSubmit={onSubmitMock}>
            {() => (
              <form>
                <Field name="name">
                  {({ input }) => <input {...input} data-testid="name" />}
                </Field>
              </form>
            )}
          </Form>,
        );
        expect(getByTestId("name")).toBeDefined();
      })