CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-testing-library--react-hooks

Simple and complete React hooks testing utilities that encourage good testing practices.

Pending
Overview
Eval results
Files

hook-rendering.mddocs/

Hook Rendering

Core functionality for rendering and testing React hooks in isolation, providing a test harness that runs hooks within a proper React component context.

Capabilities

renderHook Function

Creates a test harness for running React hooks in isolation, returning a result container and utilities for interacting with the hook.

/**
 * Renders a React hook in a test environment
 * @param callback - Function that calls the hook to be tested
 * @param options - Optional configuration including initial props and wrapper component
 * @returns RenderHookResult with current value, utilities, and async helpers
 */
function renderHook<TProps, TResult>(
  callback: (props: TProps) => TResult,
  options?: RenderHookOptions<TProps>
): RenderHookResult<TProps, TResult>;

interface RenderHookOptions<TProps> {
  /** Initial props to pass to the hook callback */
  initialProps?: TProps;
  /** React component to wrap the hook (for context providers, etc.) */
  wrapper?: React.ComponentType<TProps>;
}

interface RenderHookResult<TProps, TValue> {
  /** Container holding the hook's return value and history */
  result: RenderResult<TValue>;
  /** Re-render the hook with new props */
  rerender: (newProps?: TProps) => void;
  /** Unmount the hook and cleanup resources */
  unmount: () => void;
  /** Wait for a condition to be true */
  waitFor: (callback: () => boolean | void, options?: WaitForOptions) => Promise<void>;
  /** Wait for a selected value to change */
  waitForValueToChange: (selector: () => unknown, options?: WaitForValueToChangeOptions) => Promise<void>;
  /** Wait for the next hook update */
  waitForNextUpdate: (options?: WaitForNextUpdateOptions) => Promise<void>;
}

interface RenderResult<TValue> {
  /** Current return value of the hook */
  readonly current: TValue;
  /** Array of all values returned by the hook (including errors) */
  readonly all: Array<TValue | Error>;
  /** Current error if the hook threw an error */
  readonly error?: Error;
}

Usage Examples:

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

// Simple hook test
test("renders hook without props", () => {
  const { result } = renderHook(() => useState(0));
  
  expect(result.current[0]).toBe(0);
  expect(typeof result.current[1]).toBe("function");
});

// Hook with initial props
function useCounter(initialCount: number) {
  const [count, setCount] = useState(initialCount);
  return { count, increment: () => setCount(c => c + 1) };
}

test("renders hook with initial props", () => {
  const { result } = renderHook(() => useCounter(5));
  
  expect(result.current.count).toBe(5);
});

// Hook with wrapper component (for context)
const ThemeContext = React.createContext("light");

function useTheme() {
  return React.useContext(ThemeContext);
}

test("renders hook with wrapper", () => {
  const wrapper = ({ children }) => (
    <ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>
  );
  
  const { result } = renderHook(() => useTheme(), { wrapper });
  
  expect(result.current).toBe("dark");
});

Rerender Hook

Re-renders the hook with new props, useful for testing how hooks respond to prop changes.

/**
 * Re-render the hook with new props
 * @param newProps - New props to pass to the hook callback (optional)
 */
rerender(newProps?: TProps): void;

Usage Examples:

function useGreeting(name: string) {
  return `Hello, ${name}!`;
}

test("hook responds to prop changes", () => {
  const { result, rerender } = renderHook(
    (props) => useGreeting(props.name),
    { initialProps: { name: "Alice" } }
  );
  
  expect(result.current).toBe("Hello, Alice!");
  
  // Re-render with new props
  rerender({ name: "Bob" });
  
  expect(result.current).toBe("Hello, Bob!");
});

Unmount Hook

Unmounts the hook and cleans up any associated resources.

/**
 * Unmount the hook and cleanup resources
 */
unmount(): void;

Usage Examples:

function useInterval(callback: () => void, delay: number) {
  React.useEffect(() => {
    const interval = setInterval(callback, delay);
    return () => clearInterval(interval);
  }, [callback, delay]);
}

test("hook cleans up on unmount", () => {
  const callback = jest.fn();
  const { unmount } = renderHook(() => useInterval(callback, 100));
  
  // Let some time pass
  jest.advanceTimersByTime(250);
  expect(callback).toHaveBeenCalledTimes(2);
  
  // Unmount and verify cleanup
  unmount();
  jest.advanceTimersByTime(200);
  expect(callback).toHaveBeenCalledTimes(2); // No more calls
});

Result History

Access to the complete history of hook return values and errors.

interface RenderResult<TValue> {
  /** Current return value of the hook */
  readonly current: TValue;
  /** Array of all values returned by the hook throughout its lifecycle */
  readonly all: Array<TValue | Error>;
  /** Current error if the hook is in an error state */
  readonly error?: Error;
}

Usage Examples:

function useToggle(initial = false) {
  const [value, setValue] = useState(initial);
  const toggle = () => setValue(prev => !prev);
  return { value, toggle };
}

test("tracks hook result history", () => {
  const { result } = renderHook(() => useToggle(false));
  
  expect(result.current.value).toBe(false);
  expect(result.all).toHaveLength(1);
  expect(result.all[0]).toEqual({ value: false, toggle: expect.any(Function) });
  
  act(() => {
    result.current.toggle();
  });
  
  expect(result.current.value).toBe(true);
  expect(result.all).toHaveLength(2);
  expect(result.all[1]).toEqual({ value: true, toggle: expect.any(Function) });
});

Install with Tessl CLI

npx tessl i tessl/npm-testing-library--react-hooks

docs

act-utilities.md

async-testing.md

cleanup-management.md

error-handling.md

hook-rendering.md

index.md

server-side-rendering.md

tile.json