React Testing Basics

⭐ Interview Importance: MEDIUM
⏱️ Revision Time: 2 min

React Testing Basics

Testing React applications ensures that your UI behaves as expected, prevents regressions when you add new features, and serves as living documentation for how your components are supposed to work.

The Testing Stack

In the React ecosystem, testing usually involves a combination of three types of tools:

  1. Test Runner / Assertion Library: (e.g., Jest, Vitest)
    These tools execute your test files, provide functions to structure your tests (describe, it, test), and provide assertions to verify outcomes (expect(x).toBe(y)).

  2. DOM Environment Simulator: (e.g., JSDOM)
    React components render to a DOM. Because Node.js doesn’t have a DOM (no window or document), tools like JSDOM simulate a browser environment entirely in JavaScript so your tests can run quickly in the terminal without launching a real browser like Chrome.

  3. React Testing Utilities: (e.g., React Testing Library)
    These libraries provide specific functions to render React components into the simulated DOM, interact with them (click buttons, type text), and query the resulting output.

A Basic Test Example

Let’s look at a basic test for a <Button /> component using Vitest and React Testing Library.

// Button.jsx
export function Button({ label, onClick }) {
  return <button onClick={onClick}>{label}</button>;
}
// Button.test.jsx
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { Button } from './Button';

// 'describe' groups related tests together
describe('Button Component', () => {
  
  // 'it' or 'test' defines a single test case
  it('renders the correct label', () => {
    // 1. Render the component into the simulated DOM
    render(<Button label="Click Me" />);
    
    // 2. Query the DOM for an element containing the text
    const buttonElement = screen.getByText('Click Me');
    
    // 3. Assert that it exists
    expect(buttonElement).toBeInTheDocument();
  });

  it('calls the onClick handler when clicked', () => {
    // Create a "spy" function to track if it was called
    const handleClick = vi.fn(); 
    
    render(<Button label="Submit" onClick={handleClick} />);
    
    // Simulate a user clicking the button
    fireEvent.click(screen.getByText('Submit'));
    
    // Assert that our spy function was triggered exactly once
    expect(handleClick).toHaveBeenCalledTimes(1);
  });
});

The Arrange, Act, Assert (AAA) Pattern

Almost all good tests follow the AAA pattern, which makes them easy to read:

  1. Arrange: Set up the initial state, create mocks, and render the component.
  2. Act: Perform an action on the component (e.g., click a button, type in an input).
  3. Assert: Verify that the component’s state or the DOM output matches your expectations.

Interview Questions

Q: What is the standard “AAA” pattern in testing?
A:

  1. Arrange: Set up the test data, mock APIs, and render the components into the virtual DOM.
  2. Act: Simulate user interactions (like clicking buttons or typing into inputs).
  3. Assert: Verify that the UI updated correctly or that the expected functions were called.