loading...
Cover image for Why you should cleanup after render

Why you should cleanup after render

nicolasamabile profile image Nicolas Amabile ・2 min read

I spent some time today debugging a simple jest test with react-testing-library. I run into some issues and I couldn't easily figure out what was going on.

The problem

For a very simple component I had:

  • Snapshot test
  • Some basic interaction tests that work correctly only if I run them separately πŸ˜’

I created this example to illustrate the idea:

const Google = ({ onSubmit }) => {
  const [text, setText] = useState('')
  return (
    <Fragment>
      <input
        data-testid='textbox'
        type='text'
        value={text}
        onChange={({ target: { value }}) => setText(value)} />

        <button
          data-testid='btn'
          onClick={() => {
            if (text) {
              onSubmit(text)
              setText('')
            }
        }}>
          Search
        </button>
    </Fragment>
  )
}

And the tests:

import { render, fireEvent } from 'react-testing-library'

describe('Google tests', () => {
  test('It renders corectly', () => {
    const { container } = render(<Google />)
    expect(container.firstChild).toMatchSnapshot()
  })

  test('Search with empty value', () => {
    const onSubmit = jest.fn()
    const { container, getByTestId } = render(<Google onSubmit={onSubmit}/>)
    const button = getByTestId('btn')
    fireEvent.click(button)
    expect(onSubmit).not.toBeCalled()
  })

  test('Seach with valid value', () => {
    const onSubmit = jest.fn()
    const text = 'Example'
    const { container, getByTestId } = render(<Google onSubmit={onSubmit}/>)
    const textbox = getByTestId('textbox')
    fireEvent.change(textbox, { target: { value: text }})
    const button = getByTestId('btn')
    fireEvent.click(button)
    expect(onSubmit).toBeCalledWith(text)
  })
})

If I run this, I get this error:

Clearly, I was sending a function for that particular test ('Search with valid value'). Typo maybe? πŸ€”
My first reaction was to add .only to the test and focus on that particular problem. Guess what, it worked πŸ˜’

I spent some time debugging it until I realize that the failing test was using the component instance I created for the first snapshot test (the one that doesn't have the click handler) 🀯
How the hell did that happen?

From the official documentation:
"Failing to call cleanup when you've called render could result in a memory leak and tests which are not "idempotent" (which can lead to difficult to debug errors in your tests)."

The solution

It was as simple as using cleanup from 'react-testing-library'.

import { render, fireEvent, cleanup } from 'react-testing-library'

describe('Google tests', () => {
  beforeEach(cleanup)
  ...
})

Here you have a repl.it with the example.

Hopefully, this will save you some debugging time πŸ‘

Photo by karatara from Pexels

Posted on May 28 '19 by:

nicolasamabile profile

Nicolas Amabile

@nicolasamabile

Software dev, musician, kickboxer & bad joke teller.

Discussion

markdown guide
 

Thank you for sharing your example. This reminds me of when I realized the need of putting someMock.mockReset() in the beforeEach when you’re spying on something with Jest. Because if you don’t you might have false positives when calling expect(someMock).toHaveBeenCalled()