How to Test React Fragments

React Fragments allow you to group multiple child elements without adding extra nodes to the DOM. Testing components that utilize fragments is straightforward because fragments themselves do not render any HTML tags. This guide demonstrates how to test React Fragments using React Testing Library (RTL) and Jest, focusing on querying child elements, checking DOM structure, and utilizing snapshot testing.

Understanding Fragments in the DOM

Because React Fragments (<React.Fragment> or the shorthand <>) disappear once rendered, you cannot query the fragment itself. Instead, your tests should focus on the presence, accessibility, and behavior of the child elements contained within the fragment.

For example, consider this component:

import React from 'react';

const UserProfile = () => {
  return (
    <>
      <h1>User Profile</h1>
      <button>Edit Profile</button>
    </>
  );
};

export default UserProfile;

Querying Child Elements

When using React Testing Library, you should query the DOM elements just as a user would interact with them. Since the fragment does not introduce a wrapping div, the <h1> and <button> elements are rendered as direct children of the container’s parent element.

Here is how you test the rendering of these elements:

import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import UserProfile from './UserProfile';

test('renders child elements inside the fragment', () => {
  render(<UserProfile />);

  // Query and assert the heading is in the document
  const headingElement = screen.getByRole('heading', { name: /user profile/i });
  expect(headingElement).toBeInTheDocument();

  // Query and assert the button is in the document
  const buttonElement = screen.getByRole('button', { name: /edit profile/i });
  expect(buttonElement).toBeInTheDocument();
});

Verifying the Absence of Wrapping Elements

If you need to verify that your component does not introduce unnecessary wrapper elements (like an extra div) to the DOM, you can inspect the container’s child structure.

test('does not render a wrapping element', () => {
  const { container } = render(<UserProfile />);

  // The container's first child should be the h1, not a wrapping div
  expect(container.firstChild?.nodeName).toBe('H1');
  
  // The total number of direct children in the container should be 2
  expect(container.childElementCount).toBe(2);
});

Snapshot Testing React Fragments

Snapshot testing with Jest is an effective way to ensure that the structure of your fragments remains consistent. The generated snapshot will clearly show the elements rendered side-by-side without a parent wrapper.

import { render } from '@testing-library/react';
import UserProfile from './UserProfile';

test('matches snapshot', () => {
  const { asFragment } = render(<UserProfile />);
  expect(asFragment()).toMatchSnapshot();
});

The resulting snapshot will look like this:

<DocumentFragment>
  <h1>User Profile</h1>
  <button>Edit Profile</button>
</DocumentFragment>

Testing Keyed Fragments

When rendering lists, you must use the explicit <React.Fragment> syntax instead of the shorthand <> to assign a key prop.

const ItemList = ({ items }) => {
  return (
    dl>
      {items.map((item) => (
        <React.Fragment key={item.id}>
          <dt>{item.term}</dt>
          <dd>{item.details}</dd>
        </React.Fragment>
      ))}
    </dl>
  );
};

To test a keyed fragment, verify that the list renders the correct pairs of elements in the expected order:

test('renders list items inside keyed fragments correctly', () => {
  const mockItems = [
    { id: '1', term: 'React', details: 'A JavaScript library' },
  ];

  render(<ItemList items={mockItems} />);

  expect(screen.getByText('React')).toBeInTheDocument();
  expect(screen.getByText('A JavaScript library')).toBeInTheDocument();
});