Priority Order in RTL Queries

The RTL query methods are categorized based on priority for testing. Always aim to use the most accessible queries:

  1. getBy: Throws an error if no element is found. Use for synchronous assertions.
  2. queryBy: Returns null if no element is found. Use when testing for the absence of elements.
  3. findBy: Works with asynchronous elements that appear after some delay. It returns a promise.

Assertion Methods

RTL comes with various assertion methods to validate your queries:

  • toBeInTheDocument()
  • not.toBeInTheDocument()
  • toHaveTextContent()
  • toHaveAttribute()
  • toBeDisabled()
  • toBeEnabled()

queryBy and queryByAll

  • queryBy: Use for testing an element's absence.
  • queryByAll: Returns an empty array if no elements match. Use when checking multiple elements.

Example:

Testing for the absence of a conditional element.

test('queryBy example', () => {
  render(<App />);
  const div = screen.queryByText('Login');
  expect(div).not.toBeInTheDocument();
});

findBy Query

Use findBy for elements that appear after some delay, often in cases like API loading states.

When to Use:

When elements take time to appear (e.g., API call or animation).

Example:

test('findBy example', async () => {
  render(<App />);
  const div = await screen.findByText('data is here', {}, { timeout: 4000 });
  expect(div).toBeInTheDocument();
});

React Code:

const App = () => {
  const [data, setData] = useState(false);

  useEffect(() => {
    setTimeout(() => {
      setData(true);
    }, 2000);
  }, []);

  return <div>{data ? 'data is here' : 'data is loading'}</div>;
};

Querying Within Elements

Use within to query elements inside a specific container.

Example:

test('within example', () => {
  render(<App />);
  const ele = screen.getByText('Hello');
  expect(ele).toBeInTheDocument();

  const subEle = within(ele).getByText('hi');
  expect(subEle).toBeInTheDocument();
});

User Events

User events simulate real-world user interactions like clicks, typing, and more.

Difference Between userEvent and fireEvent:

  • fireEvent: Simulates an event but doesn’t mimic user interaction closely.
  • userEvent: More realistic, handles interactions like typing delays.

Example - Click Event:

test('userEvent example', async () => {
  userEvent.setup();
  render(<App />);
  const btn = screen.getByText('click me');
  await userEvent.click(btn);
  expect(screen.getByText('hello')).toBeInTheDocument();
});

Example - Keyboard Event:

test('keyboard event example', async () => {
  userEvent.setup();
  render(<App />);
  const input = screen.getByRole('textbox');
  await userEvent.type(input, 'shamim');
  expect(screen.getByText('shamim')).toBeInTheDocument();
});

The act Function

The act function ensures all state updates and effects are processed before assertions.

Why Use act:

To avoid warnings when testing components with asynchronous updates.

Example:

test('onChange event with act', async () => {
  userEvent.setup();
  render(<App />);
  const input = screen.getByRole('textbox');
  await act(async () => {
    await userEvent.type(input, 'shamim');
  });
  expect(screen.getByText('shamim')).toBeInTheDocument();
});

Testing Component Props

Test component props by rendering them with different values.

Example:

test('props test', () => {
  const name = 'shamim';
  render(<User name={name} />);
  const user = screen.getByText(name);
  expect(user).toBeInTheDocument();
});

Mocking Functions

Mock functions for testing callbacks or interactions.

Example:

test('mock function example', () => {
  const mockFn = jest.fn();
  render(<Button onClick={mockFn} />);
  const btn = screen.getByText('Click Me');
  userEvent.click(btn);
  expect(mockFn).toHaveBeenCalledTimes(1);
});

Debugging in React Testing Library

Use debug or prettyDOM for debugging and printing failed test cases.

Example:

test('debugging example', () => {
  render(<App />);
  screen.debug(); // Prints the DOM in console
  const btn = screen.getByText('Click Me');
  expect(btn).toBeInTheDocument();
});

Automatic Debug: Use screen.logTestingPlaygroundURL() for suggestions.

Testing Playground Chrome Extension

A Chrome DevTools extension to identify RTL queries for elements in the DOM. It simplifies query selection during testing.

Conclusion

React Testing Library offers a robust framework for testing React applications effectively. Its focus on accessibility ensures your tests are user-centric, while tools like userEvent and debug make testing and debugging seamless.