Why you should cleanup after render
Nicolas Amabile
Posted on May 28, 2019
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 👍
Posted on May 28, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.