Testing with a Custom Render

January 13, 2021

It's been quite some time for me using @testing-library/react that I can already get a sense of what's right to do and what's not.

If you have used RTL before, you probably know that it exposes a render method.

import { render } from '@testing-library/react'

This method takes two parameters:

  • A ui being the component you want to render.
  • An optional options object.
render(<Component />)

The Problem

The problem comes when you need to wrap the component you are trying to test inside some providers, or compose it with a parent component. Something like this.

render(
<Provider1>
<Provider2>
<Provider3>
<Component />
</Provider3>
</Provider2>
</Provider1>,
)

And then you need to repeat that through all the test cases:

test('...', () => {
render(
<Provider1>
<Provider2>
<Provider3>
<Component />
</Provider3>
</Provider2>
</Provider1>,
)
...
})
test('...', () => {
render(
<Provider1>
<Provider2>
<Provider3>
<Component />
</Provider3>
</Provider2>
</Provider1>,
)
...
})
test('...', () => {
render(
<Provider1>
<Provider2>
<Provider3>
<Component />
</Provider3>
</Provider2>
</Provider1>,
)
...
})

When writing our tests, we should be providing useful information to the one coming after you. And what we are doing above, is kind of repetitive and not very useful. We should care about the component we are testing, and not its implementation details (Providers are probably containing implementation details).

The Solution

The usual way to fix this, is creating a function at the top of your file or in an external file and use it through your test cases.

function renderComponent() {
return render(
<Provider1>
<Provider2>
<Provider3>
<Component />
</Provider3>
</Provider2>
</Provider1>,
)
}
test('...', () => {
renderComponent()
...
})

During my time using RTL, I've seen so many ways of solving this problem. Being these some of them:

function renderComponent() {
return render(
<Provider1>
<Provider2>
<Provider3>
<Component />
</Provider3>
</Provider2>
</Provider1>,
)
}
test('...', () => {
renderComponent()
...
})

or

function customRender() {
return render(
<Provider1>
<Provider2>
<Provider3>
<Component />
</Provider3>
</Provider2>
</Provider1>,
)
}
test('...', () => {
customRender()
...
})

or

function renderWithProviders() {
return render(
<Provider1>
<Provider2>
<Provider3>
<Component />
</Provider3>
</Provider2>
</Provider1>,
)
}
test('...', () => {
renderWithProviders()
...
})

and so many more...

But those have a big problem, they don't really tell/show the component you are testing.

What I found the best way to communicate this (also the recommended way from @testing-library), is doing the following:

import { render as rtlRender } from '@testing-library/react'
function render(ui: JSX.Element) {
return rtlRender(ui, {
wrapper: ({ children }) => {
return (
<Provider1>
<Provider2>
<Provider3>{children}</Provider3>
</Provider2>
</Provider1>
)
},
})
}
test('...', () => {
render(<Component />)
...
})
test('...', () => {
render(<Component />)
...
})

This way we can clearly see we are explicitly rendering <Component /> and we are really showing to the one coming after you what your test case is about.

Read more about

View all posts →