CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-hooks-testing-library

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

Pending
Overview
Eval results
Files

React Hooks Testing Library

React Hooks Testing Library provides simple and complete React hooks testing utilities that encourage good testing practices. It allows you to create a simple test harness for React hooks that handles running them within the body of a function component, providing various utility functions for updating inputs and retrieving outputs of custom hooks.

Package Information

  • Package Name: react-hooks-testing-library
  • Package Type: npm
  • Language: JavaScript with TypeScript definitions
  • Installation: npm install --save-dev react-hooks-testing-library

Dependencies:

  • react (peer dependency, ^16.8.0)
  • react-test-renderer (peer dependency, ^16.8.0)

Core Imports

import { renderHook, act } from "react-hooks-testing-library";
// testHook is also available but deprecated - use renderHook instead

For CommonJS:

const { renderHook, act } = require("react-hooks-testing-library");
// testHook is also available but deprecated - use renderHook instead

Basic Usage

import { renderHook, act } from "react-hooks-testing-library";
import { useState, useCallback } from "react";

// Example custom hook
function useCounter() {
  const [count, setCount] = useState(0);
  const increment = useCallback(() => setCount((x) => x + 1), []);
  const decrement = useCallback(() => setCount((x) => x - 1), []);
  return { count, increment, decrement };
}

// Test the hook
test("should increment counter", () => {
  const { result } = renderHook(() => useCounter());
  
  act(() => result.current.increment());
  
  expect(result.current.count).toBe(1);
});

// Test with async operations
test("should handle async updates", async () => {
  const { result, waitForNextUpdate } = renderHook(() => useAsyncHook());
  
  expect(result.current.loading).toBe(true);
  
  await waitForNextUpdate();
  
  expect(result.current.loading).toBe(false);
});

Capabilities

Hook Rendering

Renders a test component that calls the provided hook callback every time it renders.

/**
 * Renders a test component that calls the provided callback, including any hooks it calls
 * @param callback - Function to call each render that should call one or more hooks for testing
 * @param options - Optional configuration object
 * @returns Object with result, utilities for testing async hooks, rerendering, and unmounting
 */
function renderHook<P, R>(
  callback: (props: P) => R,
  options?: {
    initialProps?: P;
    wrapper?: React.ComponentType;
  }
): {
  readonly result: {
    readonly current: R;
    readonly error: Error;
  };
  readonly waitForNextUpdate: () => Promise<void>;
  readonly unmount: () => boolean;
  readonly rerender: (hookProps?: P) => void;
};

Usage Examples:

// Basic hook testing
const { result } = renderHook(() => useState(0));
expect(result.current[0]).toBe(0);

// Testing hooks with props
const { result, rerender } = renderHook(
  ({ initialCount }) => useState(initialCount),
  { initialProps: { initialCount: 5 } }
);
expect(result.current[0]).toBe(5);

// Rerender with new props
rerender({ initialCount: 10 });
expect(result.current[0]).toBe(5); // State is preserved, initialCount only used on first render

// Testing hooks that require context
const wrapper = ({ children }) => (
  <ThemeProvider theme="dark">{children}</ThemeProvider>
);
const { result } = renderHook(() => useTheme(), { wrapper });
expect(result.current.theme).toBe("dark");

State Updates and Effects

Wrapper around react-test-renderer's act function for handling state updates and effects.

/**
 * Ensures that updates related to state changes, effects, and event handlers are properly flushed
 * @param callback - Function to execute within act scope
 * @returns void
 */
function act(callback: () => void): void;

Usage Examples:

// Wrapping state updates
const { result } = renderHook(() => useCounter());

act(() => {
  result.current.increment();
});

expect(result.current.count).toBe(1);

// Multiple state updates
act(() => {
  result.current.increment();
  result.current.increment();
});

expect(result.current.count).toBe(3);

Async Hook Testing

Handle asynchronous operations in hooks with proper waiting mechanisms.

/**
 * Returns a Promise that resolves the next time the hook renders
 * Commonly used when state is updated as the result of an asynchronous action
 * @returns Promise that resolves on next hook render
 */
waitForNextUpdate(): Promise<void>;

Usage Examples:

// Testing async hooks
const useAsyncData = () => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    fetchData().then((result) => {
      setData(result);
      setLoading(false);
    });
  }, []);
  
  return { data, loading };
};

test("should load data asynchronously", async () => {
  const { result, waitForNextUpdate } = renderHook(() => useAsyncData());
  
  expect(result.current.loading).toBe(true);
  expect(result.current.data).toBe(null);
  
  await waitForNextUpdate();
  
  expect(result.current.loading).toBe(false);
  expect(result.current.data).toBeDefined();
});

Hook Re-rendering

Re-render the hook with new props to test how it responds to prop changes.

/**
 * Function to rerender the test component including any hooks called in the callback function
 * @param newProps - New props to pass to the callback function for future renders
 * @returns void
 */
rerender(newProps?: P): void;

Usage Examples:

// Testing prop changes
const useGreeting = (name) => {
  return `Hello, ${name}!`;
};

const { result, rerender } = renderHook(
  ({ name }) => useGreeting(name),
  { initialProps: { name: "Alice" } }
);

expect(result.current).toBe("Hello, Alice!");

rerender({ name: "Bob" });
expect(result.current).toBe("Hello, Bob!");

Hook Cleanup

Unmount the test component to trigger cleanup effects for useEffect hooks.

/**
 * Function to unmount the test component
 * Commonly used to trigger cleanup effects for useEffect hooks
 * @returns boolean indicating if unmount was successful
 */
unmount(): boolean;

Usage Examples:

// Testing cleanup effects
const useTimer = () => {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    const timer = setInterval(() => {
      setCount((c) => c + 1);
    }, 1000);
    
    return () => clearInterval(timer);
  }, []);
  
  return count;
};

test("should cleanup timer on unmount", () => {
  const { unmount } = renderHook(() => useTimer());
  
  // Verify cleanup doesn't throw errors
  expect(() => unmount()).not.toThrow();
});

Error Handling

Access errors thrown during hook execution for testing error scenarios.

/**
 * Access to any error thrown during hook execution
 */
interface HookResult<R> {
  readonly result: {
    readonly current: R;
    readonly error: Error;
  };
}

Usage Examples:

// Testing hook errors
const useErrorProneHook = (shouldThrow) => {
  if (shouldThrow) {
    throw new Error("Something went wrong");
  }
  return "success";
};

test("should capture hook errors", () => {
  const { result } = renderHook(
    ({ shouldThrow }) => useErrorProneHook(shouldThrow),
    { initialProps: { shouldThrow: true } }
  );
  
  expect(result.error).toBeInstanceOf(Error);
  expect(result.error.message).toBe("Something went wrong");
});

Types

/**
 * Options for configuring hook rendering
 */
interface RenderHookOptions<P> {
  /** Initial props passed to the hook callback */
  initialProps?: P;
  /** React component to wrap around the rendered hook */
  wrapper?: React.ComponentType;
}

/**
 * Result object returned by renderHook
 */
interface RenderHookResult<P, R> {
  /** Container for hook return value and any errors */
  readonly result: {
    /** Current return value of the hook callback */
    readonly current: R;
    /** Error thrown during hook execution, if any */
    readonly error: Error;
  };
  /** Promise that resolves on next hook render */
  readonly waitForNextUpdate: () => Promise<void>;
  /** Function to unmount the test component */
  readonly unmount: () => boolean;
  /** Function to rerender with optional new props */
  readonly rerender: (hookProps?: P) => void;
}

Deprecated APIs

testHook (Deprecated)

/**
 * @deprecated Use renderHook instead. Will be removed in a future version.
 * Legacy alias for renderHook function
 */
const testHook: typeof renderHook;

The testHook function is deprecated and shows a console warning. Use renderHook instead for all new code.

Install with Tessl CLI

npx tessl i tessl/npm-react-hooks-testing-library
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/react-hooks-testing-library@0.6.x
Publish Source
CLI
Badge
tessl/npm-react-hooks-testing-library badge