Skip to main content

Setting up Jest and React Testing Library (Part 1) #146

📖 Introduction

Having laid the conceptual groundwork with The Testing Pyramid and React, it's time to get our hands dirty and set up the tools for unit and integration testing in React. This article, Part 1 of setting up our environment, will focus on introducing Jest, a delightful JavaScript Testing Framework, and React Testing Library (RTL), a library for testing React components in a user-centric way. We'll cover why these tools are the modern standard and begin the installation process.


📚 Prerequisites

Before we begin, you should have:

  • Node.js and npm/yarn installed on your system.
  • A basic React project set up (e.g., using Create React App, Vite, or a custom Webpack configuration). If you're using Create React App, Jest and RTL are often pre-configured!
  • Familiarity with basic terminal/command-line usage.

🎯 Article Outline: What You'll Master

In this article, you will learn:

  • What is Jest? Its core features and why it's popular for JavaScript testing.
  • What is React Testing Library (RTL)? Its philosophy and how it differs from older testing approaches like Enzyme.
  • Why Jest + RTL is a Powerful Combo: How they complement each other.
  • Installation Steps: Adding Jest and RTL (and related packages) to a React project.
  • Initial Jest Configuration (Conceptual): Understanding common configuration needs.

🧠 Section 1: What is Jest? The JavaScript Testing Framework

Jest is an open-source JavaScript testing framework developed by Facebook (now Meta). It's designed to be a "delightful" testing experience, meaning it aims to be easy to set up, use, and provide fast, informative feedback.

Core Features of Jest:

  • Zero Configuration (Often): For many JavaScript projects, Jest works out of the box with minimal setup, especially for projects created with tools like Create React App.
  • Built-in Test Runner: Jest includes its own test runner, so you don't need a separate tool to execute your tests.
  • Assertion Library: It comes with a comprehensive assertion library (e.g., expect(value).toBe(expected);, expect(fn).toHaveBeenCalled();) for making claims about your code's behavior.
  • Mocking Capabilities: Powerful built-in mocking features for functions, modules, and timers, allowing you to isolate units of code for testing.
  • Snapshot Testing: A feature that lets you capture a "snapshot" of a component's rendered output (or any serializable value) and then compare future outputs against this snapshot to detect unexpected changes.
  • Code Coverage Reports: Jest can generate reports showing how much of your codebase is covered by tests.
  • Parallel Test Execution: Runs tests in parallel worker processes to maximize performance.
  • Watch Mode: Automatically re-runs tests related to files that have changed since the last commit, providing fast feedback during development.
  • Great Documentation and Community: Widely adopted, so there's plenty of community support and resources available.

While Jest can be used to test any JavaScript application (Node.js, frontend frameworks), it has become particularly dominant in the React ecosystem.


💻 Section 2: What is React Testing Library (RTL)?

React Testing Library (RTL), part of the broader "Testing Library" family of tools, is a library specifically designed for testing React components. Its core philosophy is:

"The more your tests resemble the way your software is used, the more confidence they can give you."

Key Principles and Philosophy of RTL:

  1. Test from the User's Perspective: RTL encourages you to write tests that interact with your components in the same way a user would. This means querying for elements by their accessible roles, text content, labels, etc., rather than by their internal implementation details (like component state, instance methods, or CSS selectors that are prone to change).
  2. Avoid Testing Implementation Details: By focusing on user-observable behavior, your tests become more resilient to refactoring. If you change how a component works internally but its external behavior (what the user sees and interacts with) remains the same, your RTL tests should still pass.
  3. Accessibility Focused Queries: RTL provides query methods (e.g., getByRole, getByLabelText, getByText) that encourage you to make your components more accessible. If it's hard to query for an element in your test, it might also be hard for users (especially those using assistive technologies) to interact with it.
  4. Simplicity: RTL aims to provide a lightweight and simple API.

How it Differs from Enzyme (Older Approach): Enzyme, another popular library for testing React components (now less recommended for new projects), allowed for "shallow rendering" (rendering a component one level deep without its children) and direct access to component state and props. While this offered fine-grained control, it often led to tests that were tightly coupled to the component's internal implementation, making them brittle.

RTL, by contrast, almost always renders the component within a realistic DOM environment (using jsdom via Jest) and encourages you to interact with it as a whole, just like a user.

RTL does not include its own test runner or assertion library. It's designed to be used with a test runner like Jest (or Vitest, Mocha, etc.) and Jest's assertion capabilities.


🛠️ Section 3: Why Jest + RTL is a Powerful Combination

Jest and React Testing Library are a very common and powerful pairing for testing React applications because they complement each other perfectly:

  • Jest Provides the Test Environment:
    • Test runner to execute tests.
    • Assertion library (expect).
    • Mocking capabilities.
    • Code coverage.
    • Watch mode for TDD workflows.
  • RTL Provides React-Specific Testing Utilities:
    • render function to render components into a simulated DOM.
    • User-centric query methods (getByText, getByRole, queryByTestId, etc.) to find elements.
    • fireEvent or @testing-library/user-event to simulate user interactions (clicks, typing).
    • Helper functions like waitFor to handle asynchronous updates.

Together, they provide a comprehensive solution for writing unit and integration tests for React components that are effective, maintainable, and aligned with best practices for user-centric testing.


🔬 Section 4: Installation Steps

Let's walk through adding these libraries to a typical React project. If you used Create React App (CRA) version 4+, Jest and RTL are already included and configured! For CRA 5+, you might need to install jest-environment-jsdom. If you have a custom setup (e.g., with Vite or your own Webpack config), you'll likely need to install these manually.

For Projects Not Using CRA (or needing specific versions):

  1. Install Jest:

    npm install --save-dev jest jest-environment-jsdom @types/jest
    # or
    yarn add --dev jest jest-environment-jsdom @types/jest
    • jest: The core Jest framework.
    • jest-environment-jsdom: Provides a JSDOM environment for Jest to simulate a browser DOM, which RTL needs. (Jest's default environment is Node.js).
    • @types/jest: TypeScript type definitions for Jest (even if you're not using TypeScript directly, some editors and tools benefit from these).
  2. Install React Testing Library and Related Packages:

    npm install --save-dev @testing-library/react @testing-library/jest-dom @testing-library/user-event
    # or
    yarn add --dev @testing-library/react @testing-library/jest-dom @testing-library/user-event
    • @testing-library/react: The core RTL library for React.
    • @testing-library/jest-dom: Provides custom Jest matchers to make assertions on DOM elements easier and more readable (e.g., expect(element).toBeInTheDocument(), expect(element).toHaveTextContent('...')).
    • @testing-library/user-event: A companion library to RTL that provides more realistic simulation of user interactions (typing, clicking, hovering) than RTL's built-in fireEvent. It's highly recommended.
  3. Babel Configuration (If not already handled by your setup): Jest needs to understand modern JavaScript syntax (ES6+, JSX). If your project uses Babel (common in React projects), you'll need to ensure Jest uses your Babel configuration. Install babel-jest:

    npm install --save-dev babel-jest @babel/preset-env @babel/preset-react
    # or
    yarn add --dev babel-jest @babel/preset-env @babel/preset-react

    And ensure you have a babel.config.js or .babelrc file:

    // babel.config.js
    module.exports = {
    presets: [
    '@babel/preset-env', // For modern JavaScript
    ['@babel/preset-react', { runtime: 'automatic' }] // For JSX
    ]
    };

    (Vite projects handle transpilation differently and might not need explicit Babel setup for Jest in the same way, often using esbuild or SWC. Consult Vite-specific Jest setup guides if using Vite.)

If using Create React App: As mentioned, CRA usually comes with Jest and RTL pre-configured. You typically don't need to install them separately unless you need to upgrade or customize versions. The scripts in package.json (like npm test) are already set up to use them. You might still want to add @testing-library/user-event if it's not included by default in your CRA version, as it provides better user interaction simulation.


✨ Section 5: Initial Jest Configuration (Conceptual)

Once installed, Jest looks for configuration in several places:

  • A jest.config.js file at your project root.
  • A jest key in your package.json file.

A basic jest.config.js might look like this for a React project:

// jest.config.js
module.exports = {
// The test environment that will be used for testing
testEnvironment: 'jest-environment-jsdom', // or just 'jsdom'

// A map from regular expressions to paths to transformers
// This tells Jest to use babel-jest for .js, .jsx, .ts, .tsx files
transform: {
'^.+\\.(js|jsx|ts|tsx)$': 'babel-jest',
},

// An array of glob patterns indicating a set of files for which coverage information should be collected
// collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}', '!src/**/*.d.ts'],

// The directory where Jest should output its coverage files
// coverageDirectory: 'coverage',

// A list of paths to modules that run some code to configure or set up the testing framework before each test file
setupFilesAfterEnv: ['<rootDir>/src/setupTests.js'], // Common practice

// Module file extensions for Jest to look for
moduleFileExtensions: ['js', 'jsx', 'json', 'node', 'ts', 'tsx'],

// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
moduleNameMapper: {
// Mock CSS Modules (if you use them)
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
// Mock image files
'\\.(jpg|jpeg|png|gif|webp|svg)$': '<rootDir>/__mocks__/fileMock.js',
},

// Automatically clear mock calls, instances and results before every test
clearMocks: true,
};

Key Configuration Points (to be explored further in Part 2):

  • testEnvironment: Set to 'jsdom' or 'jest-environment-jsdom' for React component testing.
  • transform: Tells Jest how to process your files (e.g., using babel-jest for JSX/ES6+).
  • setupFilesAfterEnv: Points to a setup file that runs before each test suite. This is where you typically import @testing-library/jest-dom to extend Jest's matchers.
  • moduleNameMapper: Used to mock non-JavaScript imports like CSS files or images, which Jest can't process directly. identity-obj-proxy is common for CSS Modules, and you'd create a simple file mock for images.

Creating src/setupTests.js: This file is commonly used to extend Jest's expect with matchers from @testing-library/jest-dom.

// src/setupTests.js
import '@testing-library/jest-dom';
// You can add other global setup here if needed

Creating __mocks__/fileMock.js (for moduleNameMapper):

// __mocks__/fileMock.js
module.exports = 'test-file-stub';

This simple mock just returns a string placeholder when an image (or other configured file type) is imported in a test.

We'll dive deeper into practical configuration and writing our first tests in Part 2.


💡 Conclusion & Key Takeaways (Part 1)

We've introduced the primary tools for testing React applications: Jest as the JavaScript testing framework and React Testing Library (RTL) for user-centric component testing. Understanding their individual roles and how they complement each other is the first step towards building a robust testing suite. We've also covered the initial installation process.

Key Takeaways:

  • Jest provides the overall testing infrastructure (runner, assertions, mocking).
  • React Testing Library (RTL) provides utilities to render and interact with React components from a user's perspective, avoiding implementation details.
  • The combination of Jest and RTL is the modern standard for unit and integration testing in React.
  • Installation involves adding several dev dependencies, and configuration might be needed for projects not using Create React App.

➡️ Next Steps

With the tools introduced and initial installation covered, "Setting up Jest and React Testing Library (Part 2)" will guide you through:

  • Finalizing the Jest configuration.
  • Adding a test script to your package.json.
  • Writing and running your very first simple React component test using Jest and RTL.

Get ready to make your testing setup operational!


glossary

  • Jest: A JavaScript testing framework known for its ease of use, built-in runner, assertion library, and mocking capabilities.
  • React Testing Library (RTL): A library for testing React components by interacting with them as a user would, focusing on behavior rather than implementation details.
  • Test Runner: A tool that executes test files and reports their results.
  • Assertion Library: A library that provides functions (e.g., expect(a).toBe(b)) to make claims about your code's behavior in tests.
  • Mocking: Replacing parts of your code (functions, modules) with controlled fakes during tests to isolate the unit under test.
  • Snapshot Testing: A Jest feature where a component's output (or any value) is saved to a file and compared against future outputs to detect changes.
  • Code Coverage: A metric indicating the percentage of your codebase that is executed by your automated tests.
  • JSDOM: A JavaScript implementation of the WHATWG DOM and HTML standards, used to simulate a browser environment for testing frontend code without needing an actual browser.
  • @testing-library/jest-dom: Extends Jest with custom matchers for easier DOM assertions (e.g., toBeInTheDocument).
  • @testing-library/user-event: Simulates user interactions more realistically than RTL's fireEvent.

Further Reading