Skip to content

React Testing Hooks with `renderHook`

Published:

Basic Setup

  1. Install the testing library for hooks:

    npm install @testing-library/react-hooks

    Note: This library provides utilities to test custom React hooks effectively.

  2. Import renderHook and act in your test file:

    import { renderHook, act } from "@testing-library/react-hooks";

    Tip: act is essential for ensuring that all updates related to state and effects are processed correctly.

Basic Usage

  • Testing a simple hook:

    const { result } = renderHook(() => useCustomHook());
    
    expect(result.current.someValue).toBe(initialValue);

    Detail: result.current gives you access to the current state of the hook.

Updating Hook State

  • When the hook involves state updates, use act:

    const { result } = renderHook(() => useCounter());
    
    act(() => {
      result.current.increment();
    });
    
    expect(result.current.count).toBe(1);

    Best Practice: Always wrap state updates in act to ensure proper state management.

Testing with Props

  • Pass props to the hook and update them:

    const { result, rerender } = renderHook(({ value }) => useCustomHook(value), {
      initialProps: { value: 0 },
    });
    
    expect(result.current.value).toBe(0);
    
    // Update the prop
    rerender({ value: 1 });
    expect(result.current.value).toBe(1);

    Tip: Use rerender to simulate prop changes and test how the hook responds.

Testing Async Hooks

  • Handling asynchronous code with hooks:

    const { result, waitForNextUpdate } = renderHook(() => useAsyncHook());
    
    expect(result.current.loading).toBe(true);
    
    // Wait for the next state update
    await waitForNextUpdate();
    
    expect(result.current.loading).toBe(false);
    expect(result.current.data).toEqual(expectedData);

    Detail: waitForNextUpdate is crucial for testing hooks that perform asynchronous operations.

Using waitFor

  • For hooks that might update multiple times:

    import { waitFor } from "@testing-library/react";
    
    const { result } = renderHook(() => useDebouncedValue(input));
    
    await waitFor(() => {
      expect(result.current).toBe(expectedValue);
    });

    Note: waitFor allows you to wait for a specific condition to be met, which is useful for debounced values.

Cleaning Up After Tests

  • To ensure proper cleanup between tests:

    afterEach(() => {
      // Clean up any effects left behind
      cleanup();
    });

    Best Practice: Always clean up after tests to prevent side effects from affecting other tests.

Mocking External Dependencies

  • Example with jest.mock:

    jest.mock("some-external-library", () => ({
      someFunction: jest.fn().mockReturnValue(mockValue),
    }));

    Tip: Mocking external dependencies helps isolate your tests and avoid side effects from real implementations.

Summary of Methods

  • renderHook: Render a hook function and return an object containing the result and helper methods.
  • act: Wrap state updates to ensure React’s state batching works correctly.
  • rerender: Re-render the hook with new props.
  • waitForNextUpdate: Wait for the next state update.
  • waitFor: Wait for a condition to be met.

References