Why should we not use document.querySelector() while testing React components.

Harshul Kansal
smallcase Engineering
3 min readJul 6, 2022

--

Why do we need to target the element?

While writing a unit test case for the react component, first we need to target the smaller sub-components and write the tests for the sub-components which might include:

  • checking the existence in the document
  • perform various events on them if required as per the written logic
  • checking if the output is as expected, etc.

there are various ways to target the element provided to the user by different libraries. One of them is a JS query: document.querySelector('')`, which we are going to talk about in this article.

Why do we want to avoid document.querySelector()?

As the RTL philosophy suggests, we want to avoid selecting elements by classNames, ids, or even test ids, because that is not how the user would find the element on the screen.

querySelector as a query strategy is vulnerable to DOM changes or if we are basing the selectors on CSS Styling, the CSS Styling locators can change independently of the functionality and we may find functional-based tests failing due to styling changes.

Using `screen` and avoiding container:

The base queries from DOM Testing Library require you to pass a container as the first argument. Most framework implementations of Testing Library provide a pre-bound version of these queries when you render your components with them which means you do not have to provide a container. As explained in the following example.

While using querySelector we have to make an extra container:

Example:

// HTML to be tested
<body>
<div id="app">
<label for="username-input">Username</label>
<input id="username-input" />
</div>
</body>

Required to create a container and then pass that as the first argument. 👇

import { getByLabelText } from '@testing-library/dom';// Need to provide a container:
const container = document.querySelector('#app')
const inputNode2 = getByLabelText(container, 'Username')

Instead of this 👆 , we can use screen from '@testing-library/dom’ 👇

import { screen, getByLabelText } from '@testing-library/dom';

// With screen:
const inputNode1 = screen.getByLabelText('Username')

The workaround: (not querySelector, then what?)

Using querySelector as an escape hatch to query by class or id is not recommended because they are invisible to the user. Use a testid if you have to but that too should be kept as a last resort to target an element because:

  • The user cannot see (or hear) these, so this is only recommended for cases where you can’t match by role or text or it doesn’t make sense (e.g. when the text is dynamic)
  • We have various other queries that can be used instead of testid that are accessibility friendly as well. Example:
  • getByRole() : (Highly recommended) This can be used to query every element that is exposed in the accessibility tree. With the name option you can filter the returned elements by their accessible name. (if you can’t, it’s possible your UI is inaccessible). E.g:getByRole(‘button’, {name: /submit/i}) You may check the list of the roles here.
  • getByLabelText, getByPlaceholderText, getByText, getByDisplayValue .

Also some semantic queries such as:

  • getByAltText If your element is one that supports alt text (img, area, input, and any custom element), then you can use this to find that element.
  • getByTitle: The title attribute is not consistently read by screen readers and is not visible by default for sighted users.

Helper extensions:

For targetting the elements you may use the extension:

  • Testing Library: which query: Finds the suggested query to use in your tests, you may copy queries from any web page.
  • Testing Playground: Adds Testing-Playground to the Chrome Developer Tools. Testing Playground is an extension that helps you find the best queries to select elements for the open-source Testing-Library family. It allows you to inspect the element hierarchies in the Chrome Developer Tools, and provides you with suggestions on how to select them while encouraging good testing practices. You will get two new tabs in your Chrome DevTools: “🐸 Testing Playground” as a tab, and as a side panel under the “Elements” tab.

--

--

Harshul Kansal
smallcase Engineering

Self motivated Javascript developer, working towards achieving the best coding style possible.