Formik & React Testing Library and Screaming at a Computer for an Hour
Tags: react, testing, formik, typescript
Published: 2019-12-16 01:37:38

I have loved using both [Formik](https://jaredpalmer.com/formik/) and [React Testing Library](https://testing-library.com/). At this point in the React community, I consider these tools to be nice sensible defaults of projects of any real size.

This afternoon, I needed to write some unit tests for some components that I had to add to my project and they were horribly broken.

```tsx
import * as React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { Formik, Form, Field } from 'formik';
import { render, fireEvent, waitForElement } from '@testing-library/react';

describe('Very important form', () => {
  it('submits values and fires', async () => {
    const mock = jest.fn();
    const { getByText, getByTestId } = render(
      <Formik initialValues={{ name: '' }} onSubmit={mock}>
        <Form>
          <Field name="name" data-testid="Input" />
          <button type="submit">Submit</button>
        </Form>
      </Formik>
    );

    const input = await waitForElement(() => getByTestId('Input'));
    const button = await waitForElement(() => getByText('Submit'));

    fireEvent.change(input, {
      target: {
        value: 'Charles',
      },
    });

    fireEvent.click(button);

    expect(mock).toBeCalled();
    expect(mock.mock.calls[0][0].name).toBe('Charles');
  });
});

```

What I want to validate is that `mock` was called when the form submits and to see the results of `onSubmit` include the value I typed.

## Identifying the Problem

### ⛔️ Jest's Cache 

Normally, when I have tests like this don't pass, where everything looks good, I start to blame Jest itself. Jest has a pretty heavy caching system. This allows you to continually be watching test files and run them very quickly and its cache is a good thing. But occasionally (am empirically) that cache gives false positives and I have found that clearing this cache and re-running your tests can give you the validation that your tests are rightfully passing. You can do that by running:

```
jest --clearCache
```

And typically in your CI process (like Travis or GitHub Actions), you should include:

```
jest --no-cache
```

But running your tests locally on your machine, caching is a good thing.

And with the cache cleared, still broken.

### ⛔️ Maybe `act()`

React DOM's test utils package (`react-dom/test-utils`) has a utility called `act()` and React Testing Library has a wrapper around it too. `act()` (from what I understand) prepares a container to be updated by batching all updates like the way it would work in the browser. So things like updating the state or re-rendering components should be wrapped in `act()`.

Anytime you're performing an async operation, it's helpful to wrap things in `act()` and it's covered in the [FAQ](https://testing-library.com/docs/react-testing-library/faq) section of React Testing Library.

Wrapping the events that update the component like this:

```tsx
import { act, fireEvent } from '@testing-library/react';


act(() => {
  fireEvent.change(input, {
    target: {
      value: 'Charles',
    },
  });
});

act(() => {
  fireEvent.click(button);
});

```

Didn't help, still broken.


### ⛔️ User Error

At this point, I read through (perhaps too quickly) both Formik and React Testing Library's documentation sites and not finding anything that stood out about the tests I was writing being wrong or missing anything.

I read through all of the documentation from Jest on using `jest.fn()` mocks. And nothing. 😭

At this point, I was incredibly frustrated, I uttered every swear word at an array of volumes at my computer and even perhaps invented new swear words. I contemplated switching professions, I went for a walk around the office and drank a glass of water. 🤬

My tests were still broken. 😤

### ✅ A mysterious solution found buried in GitHub Issues 

Then I searched for "React Testing Library" in the Issues section of the Formik repo and found this [#1554](https://github.com/jaredpalmer/formik/issues/1554). Since, Formik runs it's validations internally, async, then calls the `onSubmit` props, we need to await the results. React Testing Library gives us a utility for this it's called [`wait()`](https://testing-library.com/docs/dom-testing-library/api-async#wait). We need to wait to see if `mock` is called and to checkout the results.

The solution looks like this:

```tsx

import * as React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { Formik, Form, Field } from 'formik';
import { render, fireEvent, waitForElement, wait } from '@testing-library/react';

describe('Very important form', () => {
  it('submits values and fires', async () => {
    const mock = jest.fn();
    const { getByText, getByTestId } = render(
      <Formik initialValues={{ name: '' }} onSubmit={mock}>
        <Form>
          <Field name="name" data-testid="Input" />
          <button type="submit">Submit</button>
        </Form>
      </Formik>
    );

    const input = await waitForElement(() => getByTestId('Input'));
    const button = await waitForElement(() => getByText('Submit'));

    fireEvent.change(input, {
      target: {
        value: 'Charles',
      },
    });

    fireEvent.click(button);

    wait(() => {
      expect(mock).toBeCalled();
      expect(mock.mock.calls[0][0].name).toBe('Charles');
    });
  });
});

```

And now my tests are passing.


## Conclusion


Feel your feelings. If your tests aren't passing with either of these libraries, see if using `wait()` or `act()` might help and swearing at your computer isn't the worst thing in the world but having a glass of water is also a good idea.

---

_Sign up for my [newsletter](https://tinyletter.com/charlespeters), follow me on Twitter [@charlespeters](https://twitter.com/charlespeters) or find me at [charlespeters.net](https://charlespeters.net/)._