Building Confidence with User-Centric Approaches and DOM Testing Integration in React Testing Library

Tay Bencardino
4 min readDec 23, 2023

Any specific reason for using @testing-library ?

You want to write maintainable tests that give you high confidence that your components are working for your users. As a part of this goal, you want your tests to avoid including implementation details so refactors of your components (changes to implementation but not functionality) don’t break your tests and slow you and your team down.

React Testing Library is a JavaScript testing utility specifically designed for testing React components. It is built on top of the popular testing library called DOM Testing Library, which focuses on testing applications in a way that resembles how users interact with them. The philosophy behind React Testing Library is to encourage developers to write tests that closely mimic how users interact with the application, leading to more robust and maintainable tests.

User-Centric Testing

In the context of React Testing Library, it refers to an approach where tests are written to simulate user interactions and behaviours with the application rather than focusing on the internal implementation details of the components. The goal is to ensure that the components behave as expected from a user’s perspective, leading to tests that are more resilient to changes in the implementation.

Here’s an example:

// LoginForm.tsx
import { useState } from "react";

interface LoginFormProps {
onLogin: (username: string) => void;
}

const LoginForm = ({ onLogin }: LoginFormProps) => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");

const handleLogin = () => {
if (username === "user" && password === "password") {
onLogin(username);
} else {
setError("Invalid username or password");
}
};

return (
<div>
<input
type="text"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button onClick={handleLogin}>Login</button>
{error && <div>{error}</div>}
</div>
);
};

export default LoginForm;
// LoginForm.spec.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import LoginForm from './LoginForm';

const mockOnLogin = jest.fn();

it('User can successfully log in with valid credentials', () => {
render(<LoginForm onLogin={mockOnLogin} />);

const usernameInput = screen.getByPlaceholderText('Username')
const passwordInput = screen.getByPlaceholderText('Password')
const loginButton = screen.getByText('Login')

fireEvent.change(usernameInput, { target: { value: 'user' } });
fireEvent.change(passwordInput, { target: { value: 'password' } });
fireEvent.click(loginButton);

expect(mockOnLogin).toHaveBeenCalledWith('user');
});

The test simulates user interactions by changing the input values and clicking the “Login” button. The expectation (expect) checks that the onLogin function is called with the correct username when valid credentials are provided.

This test is focused on the user’s perspective, ensuring that the login functionality works as expected rather than checking the internal details of the LoginForm component.

DOM Testing Library

Integrating the DOM Testing Library within React Testing Library is a key aspect that enhances the effectiveness of testing React components. DOM Testing Library is a generic testing library that focuses on interacting with the DOM in a way that closely simulates how users interact with a web page. React Testing Library extends and adapts these principles specifically for testing React components.

☝️ Key Points:

  • DOM Testing Library promotes a user-centric testing approach, emphasizing that tests should mirror how users interact with the application. This involves querying and interacting with the DOM elements in a manner that reflects real-world user behaviour.
  • React Testing Library builds on top of DOM Testing Library, incorporating its principles and utilities. This integration allows developers to use a set of functions provided by the DOM Testing Library to query and manipulate React components.
  • React Testing Library provides a set of query functions that internally use DOM Testing Library queries. These functions allow you to select elements in the rendered React components using meaningful criteria from a user’s perspective. Examples include getByLabelText, getByText, getAllByRole, and more.
  • DOM Testing Library focuses on realistic interactions, and this philosophy carries over to React Testing Library. For example, rather than testing internal component states directly, React Testing Library encourages testing what a user would see or do, such as clicking buttons, entering text, and verifying that certain elements are present on the screen.
// Counting.tsx
import { useState } from "react";

const Counting = () => {
const [count, setCount] = useState<number>(0);

const increase = () => {
setCount(count + 1);
};


return (
<div>
<button onClick={increase}>INCREASE</button>
<p>Count: {count}</p>
</div>
);
};

export default Counting;
// Counting.spec.tsx
import { render, fireEvent, screen } from "@testing-library/react";
import Counter from "../Counting";

it("Counter increments when the button is clicked", () => {
render(<Counter />);

expect(screen.getByText("Count: 0")).toBeInTheDocument();

fireEvent.click(screen.getByText("INCREASE"));

expect(screen.getByText("Count: 1")).toBeInTheDocument();
});

In this example, the getByText query from React Testing Library, which internally uses DOM Testing Library, is used to select the button by its visible text content. The test then simulates a user's click on the button and asserts that the count is updated accordingly.

It demonstrates how React Testing Library integrates with DOM Testing Library principles to facilitate user-centric testing of React components. The focus is on testing users' behaviour rather than internal implementation details.

In conclusion, leveraging React Testing Library, and its integration with DOM Testing Library, brings several best practices to the forefront, ensuring robust and user-centric testing for React applications.

The primary motivation for using React Testing Library lies in the pursuit of writing maintainable tests that instil high confidence in the functionality of components without being overly tied to implementation details. By adopting a user-centric testing approach, developers can create tests that closely mimic real-world user interactions, making them more resilient to changes in the implementation.

In essence, the use of the React Testing Library, guided by user-centric testing principles and its integration with DOM Testing Library, contributes to the development of reliable, maintainable, and user-focused test suites for React components. This approach not only enhances the efficiency of testing workflows but also ensures the resilience of tests in the face of evolving implementations.

--

--