Screen vs. render queries
Asked Answered
S

2

88

There are two ways to use queries using React Testing Library.

You can either use the queries returned by the render method:

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

...

const { getByText } = render(<div>Foo</div>)

expect(getByText('Foo')).toBeInTheDocument()

Or you can use the screen object:

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

...

render(<div>Foo</div>)

expect(screen.getByText('Foo')).toBeInTheDocument()

But there is no indication in the documentation about which one is the best option to use and why.

Can someone enlighten me?

Sandell answered 28/4, 2020 at 14:17 Comment(0)
H
104

The latest recommended option by the react-testing-library author Kent C. Dodds himself is to use screen.

The benefit of using screen is you no longer need to keep the render call destructure up-to-date as you add/remove the queries you need. You only need to type screen. and let your editor's magic autocomplete take care of the rest.

The only exception to this is if you're setting the container or baseElement which you probably should avoid doing (I honestly can't think of a legitimate use case for those options anymore and they only exist for historical reasons at this point).

Source: https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#not-using-screen

Hydraulics answered 7/5, 2020 at 6:48 Comment(10)
What does this part mean? keep the render call destructure up-to-date as you add/remove the queries you needScrivener
@BradyDowling render returns an object that you can destructure, e.g. const { getByText } = render(<MyComponent />). If you want to use another query, you would have to update this destructured object: const { getByText, getByLabelText } = render(<MyComponent />). With screen, all the methods are available on the object itself (screen.getByText, screen.getByLabelText, etc.)Shuster
@JoeyM-H With screen, all the methods are available on the object itself It seems like this is already true of the component that's returned from render. Seems like you can just skip the destructuring of the rendered component and get the same benefit, but I believe I am missing something important.Scrivener
@BradyDowling you're right, but it's still a little less verbose to use a globally imported screen instead of assigning the return from render() to a variable every time, especially if you're writing a lot of tests in one file. Here's the PR of the feature, with a longer explanation from the author github.com/testing-library/dom-testing-library/pull/412 Apparently it helps avoid some other edge cases too.Shuster
From the discussion, it seems like the returned object from the render function could be framework specific but the screen object will be framework agnostic, making your tests more resilient and consistent across projects.Scrivener
Fwiw if you are using the return value from render, I find it less work in the long run (and at least as clear in the code), not to destructure it.Lannie
If having to add methods to the destructuring is the only hassle then I'll choose render. Wasn't that the purpose of destructuring? To do this const { methods } = screen instead of this screen.method(), screen.method, ...?Cuttie
you mentioned to benefit of using screen and not the exact difference of between themVenu
@MartinRützy OP didn't ask what the difference was, they asked which was better.Feodore
you can do this const wrapper = render(<MyComponent/>) and it will be the same as screen, so what is the man different between them !Fifteen
C
18

screen is provided by @testing-library/dom, which is what @testing-library/react is built upon. When using the screen methods, they will query within the html <body> element, as described in the docs:

Because querying the entire document.body is very common, DOM Testing Library also exports a screen object which has every query that is pre-bound to document.body

render() is only in @testing-library/react. It returns an object similar to screen and the default is to also bind the queries to the <body>. By default, there is little difference, but you can customize its behavior by passing in options.

For example, you can specify an element other than the <body> to query within, and can even provide custom query methods.


Update:

Read the link in the answer from @rrebase

The maintainers of the project now recommend using screen, and they likely know much better than I do.

I am leaving my original recommendation for using render below if you would still like to read it.


To answer your question of which is the best, I would say using render() is better because the options make it more flexible, but to quote the docs:

You won't often need to specify options

Still, my preference would be to use the methods provided by render(), because if you ever decide to add in options, you wont need to remember to change all your queries.

Cutup answered 28/4, 2020 at 15:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.