loading...

Updating Formik fields when testing with Jest and Enzyme or react-testing-library

dannypule profile image Danny Pule ・2 min read

Here is an example test suite using Jest and Enzyme but you could just as easily use another testing framework like react-testing-library

// Versions used:
react: 16.13.1
jest: 24.9.0
enzyme: 3.11.0
formik: 1.5.8
// Helper function (entirely optional) - when mounting 
// components that use the store, theming etc we need 
// to wrap our components in their relevant providers
export const TestApp = ({ children, ...rest }: { children: React.ReactNode }) => {
  const store = configureStore();
  return (
    <Provider store={store}>
      <IntlProvider>
        <ThemeProvider theme={theme}>
          <BrowserRouter>
            {React.Children.map(children, (child) =>
              React.isValidElement(child) ? React.cloneElement(child, rest) : child
            )}
          </BrowserRouter>
        </ThemeProvider>
      </IntlProvider>
    </Provider>
  );
};

// Helper function - uses act() under the hood from react-dom/test-utils
const actImmediate = (wrapper: ReactWrapper) =>
  act(
    () =>
      new Promise<void>((resolve) => {
        setImmediate(() => {
          wrapper.update();
          resolve();
        });
      })
  );

// Helper function - sets Formik fields directly using it's instance
const setFormikFields = (wrapper: ReactWrapper, values: Record<string, string>) => {
  return new Promise((resolve) =>
    wrapper
      .find('Formik')
      .instance()
      .setState({ values }, () => {
        wrapper.update();
        resolve();
      })
  );
};

const updateLoginMock = jest.fn();

// data-qa tags for easy element selection within the component
const qa = {
  form: '[data-qa="form"]',
  passwordField: '[data-qa="passwordField"]',
  emailField: '[data-qa="emailField"]',
};

describe('Given a MyComponent component', () => {
  let wrapper: ReactWrapper;

  describe('When it is rendered', () => {
    beforeEach(() => {
      wrapper = mount(
        <TestApp>
          <MyComponent updateLogin={updateLoginMock} />
        </TestApp>
      );
    });

    describe('When the form fields receive a value', () => {
      const fields = {
        password: 'Pass123$',
        email: 'some@email.com',
      };

      beforeEach(async () => {
        await setFormikFields(wrapper, fields); // sets the form fields directly using Formik's instance. This happens asynchronously so we need to use async/await
      });

      it('Then the form fields should have the correct value', () => {
        expect(wrapper.find(qa.passwordField).prop('value')).toBe(fields.password);
        expect(wrapper.find(qa.emailField).prop('value')).toBe(fields.email);
      });

      describe('When the form is submitted (form is valid)', () => {
        beforeEach(async () => {
          wrapper.find(qa.form).simulate('submit');

          await actImmediate(wrapper); // uses act() under the hood from react-dom/test-utils
        });

        it('Then updateEmailMock should be called with the correct data', () => {
          expect(updateEmailMock).toHaveBeenCalledWith({ password: fields.password, email: fields.email });
        });
      });
    });
  });
});

Posted on May 7 by:

dannypule profile

Danny Pule

@dannypule

Senior Frontend Software Engineer

Discussion

markdown guide