How to Mock React Refs

Mocking React refs is a common necessity when writing unit tests for components that interact directly with the DOM or external libraries. This article provides a direct, step-by-step guide on how to mock React refs using Jest and React Testing Library, covering scenarios like mocking useRef hooks, stubbing DOM methods, and testing forwarded refs.

Mocking DOM Methods on Refs

The most common reason to mock a ref is to test component behavior that triggers a DOM method, such as .focus(), .scrollIntoView(), or .play() on a video element. Instead of mocking the React hook itself, you can mock the DOM prototype method before rendering the component.

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

describe('MyComponent', () => {
  it('should focus the input on mount', () => {
    // Mock the focus method on the HTMLInputElement prototype
    const focusMock = jest.fn();
    HTMLInputElement.prototype.focus = focusMock;

    render(<MyComponent />);

    expect(focusMock).toHaveBeenCalledTimes(1);
  });
});

Mocking the useRef Hook

If you need to control the exact value of ref.current during a test, you can mock the useRef hook directly using jest.spyOn. This is useful when your component logic depends on specific properties of a ref that are difficult to simulate in a test environment.

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

describe('MyComponent with useRef mock', () => {
  it('should use the mocked ref value', () => {
    const mockRef = {
      current: {
        getBoundingClientRect: () => ({ width: 100, height: 50 }),
      },
    };

    // Spy on React.useRef and return the mock object
    jest.spyOn(React, 'useRef').mockReturnValue(mockRef);

    render(<MyComponent />);
    
    // Assertions based on the mocked bounding rect values
  });
});

Mocking Forwarded Refs

When testing components wrapped in React.forwardRef, you can pass a mocked ref object directly as a prop from your test suite. This allows you to verify that the child component correctly assigns the ref to its underlying DOM element.

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

describe('ForwardedInput', () => {
  it('assigns the ref to the input element', () => {
    const ref = React.createRef();
    render(<ForwardedInput ref={ref} />);

    expect(ref.current).toBeInstanceOf(HTMLInputElement);
  });
});