When testing components with React Testing Library, I find myself starting with getBy*
, and occasionally needing to replace it with queryBy*
(for example if I need to check for the non-existence of an element). My tests end up with a mix of getBy
and queryBy
, and I've recently just been using queryBy
for everything.
It got me thinking... is there ever a reason to use getBy
?
Assertions like this fail as expected, without the need to throw an error:
expect(queryByText('Click me')).toBeInTheDocument();
expect(queryByLabel('Name').value).toBe('George')
What's the advantage of throwing an error if an element isn't found, and is there a reason not to use queryBy
for all (synchronous) queries?
EDIT: It looks like queryBy
is now only recommended for asserting that something is not in the document:
https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#using-query-variants-for-anything-except-checking-for-non-existence
The article also recommends using screen.queryBy
/screen.getBy
rather than destructuring from render
, which simplifies changing from one to another, since you no longer have to update the destructured function.
You can not do that with RTL. You are not supposed to interact with the internals of your components.
Using without Jest However, most people using React Testing Library are using it with the Jest testing framework with the testEnvironment set to jest-environment-jsdom (which is the default configuration with Jest 26 and earlier). jsdom is a pure JavaScript implementation of the DOM and browser APIs that runs in Node.
Note – you can also use it without Jest. "The more your tests resemble the way your software is used, the more confidence they can give you." So, let's start using it in the next section. By the way, you don't need to install any packages, since create-react-app comes with the library and its dependencies.
If you want mimic real-world user interactions, the React Testing Library is the way to go because you can do the same with fireEvent functions. Meanwhile, Enzyme is better suited to situations where you have to match the state of React or some other function with state.
As you've stated, the difference between getBy* and queryBy* is that getBy* throws an error if the element is not found and queryBy* does not. For me, if I'm expecting something to be there, I always use getBy* and only use queryBy* in scenarios where I'm asserting that something isn't there. If an element isn't present that I'm expecting to be, I want to know about it as early as possible in the test, which is wherever the getBy* call is made.
So I would say the advantage of throwing the error is that you always ensure your test failure will point to the root problem (not being able to find an element you expect to be there) as opposed to a side effect of that root problem (trying to use that element for something later in the test).
Example Test:
const { getByTestId, queryByTestId } = render(getComponent());
const textInput = queryByTestId("textInput");
fireEvent.change(textInput, { target: { value: "hello" } });
fireEvent.change(textInput, { target: { value: "hi" } });
Using queryByTestId, the test output is:
Unable to fire a "change" event - please provide a DOM element.
23 | const textInput = queryByTestId("textInput") as any;
24 |
> 25 | fireEvent.change(textInput, { target: { value: "hello" } });
| ^
26 | fireEvent.change(textInput, { target: { value: "hi" } });
27 |
So it does indicate that textInput
wasn't found. If I change it to getByTestId, the output is
Unable to find an element by: [data-testid="textInput"]
<body>
<div>
<div>
<button
type="button"
>
Show the Text Input!
</button>
</div>
</div>
</body>
21 | const { getByTestId, queryByTestId, rerender } = render(getComponent());
22 |
> 23 | const textInput = getByTestId("textInput") as any;
| ^
24 |
25 | fireEvent.change(textInput, { target: { value: "hello" } });
26 | fireEvent.change(textInput, { target: { value: "hi" } });
So the getBy* error output has two advantages in my mind:
These marginal dev experience improvements are worth using the getBy* varieties as the default for me. Typically I'm only using getBy* in my tests and only using queryBy* when it's important to assert that something isn't present. It is certainly possible to just use queryBy* though and you are free to do so if you find the cost of using both outweighs the benefits.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With