How to Test useDebugValue Hook in React

Testing the useDebugValue hook in React ensures that your custom hooks provide the correct debugging labels within the React Developer Tools. This article explains how the useDebugValue hook works and provides a step-by-step guide on how to write unit tests to verify its behavior using Jest and React Testing Library.

Understanding useDebugValue

The useDebugValue hook is used to display a label for custom hooks in React DevTools. It does not affect the runtime behavior of your application or return any value. Because its only side effect is UI-related within the DevTools extension, you cannot test it by asserting against the rendered DOM. Instead, you must test it by spying on the hook’s execution.

Here is a simple custom hook that uses useDebugValue:

import { useState, useDebugValue } from 'react';

export function useFriendStatus(friendId) {
  const [isOnline, setIsOnline] = useState(null);

  // Show a label in DevTools next to this hook
  useDebugValue(isOnline ? 'Online' : 'Offline');

  return isOnline;
}

How to Test useDebugValue with Jest

To test that useDebugValue is called with the correct arguments, you need to spy on React.useDebugValue before rendering your custom hook using @testing-library/react.

Below is the complete testing setup:

1. The Test Code

import React from 'react';
import { renderHook, act } from '@testing-library/react';
import { useFriendStatus } from './useFriendStatus';

describe('useFriendStatus Debug Value', () => {
  let useDebugValueSpy;

  beforeEach(() => {
    // Spy on React.useDebugValue
    useDebugValueSpy = jest.spyOn(React, 'useDebugValue');
  });

  afterEach(() => {
    // Clear the spy to avoid polluting other tests
    useDebugValueSpy.mockRestore();
  });

  it('should call useDebugValue with the correct status', () => {
    // Render the hook
    const { result } = renderHook(() => useFriendStatus(1));

    // Initially, isOnline is null, which evaluates to 'Offline'
    expect(useDebugValueSpy).toHaveBeenCalledWith('Offline');
  });
});

2. Testing with Formatting Functions

Often, formatting a debug value can be an expensive operation. To optimize performance, useDebugValue accepts an optional formatting function as a second argument. This function only runs if the DevTools are actually open.

Here is how you define a hook with a formatting function:

import { useState, useDebugValue } from 'react';

export function useDateHook() {
  const [date] = useState(new Date());

  // The formatting function only runs when inspected
  useDebugValue(date, d => d.toDateString());

  return date;
}

To test this scenario, you need to assert that useDebugValue was called with both the state and the formatting function, and then verify the formatting function logic independently:

import React from 'react';
import { renderHook } from '@testing-library/react';
import { useDateHook } from './useDateHook';

describe('useDateHook Debug Value', () => {
  it('should pass the date and formatting function to useDebugValue', () => {
    const useDebugValueSpy = jest.spyOn(React, 'useDebugValue');
    
    renderHook(() => useDateHook());

    // Verify useDebugValue was called with a Date object and a function
    expect(useDebugValueSpy).toHaveBeenCalledWith(
      expect.any(Date),
      expect.any(Function)
    );

    // Retrieve the formatting function passed to the hook
    const formatter = useDebugValueSpy.mock.calls[0][1];
    const testDate = new Date('2026-03-29');

    // Test the formatter function output directly
    expect(formatter(testDate)).toBe('Sun Mar 29 2026');

    useDebugValueSpy.mockRestore();
  });
});