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

error-handling.mddocs/

Error Handling

Error suppression and handling utilities for testing error scenarios and managing console output during tests. These utilities help create clean test environments and properly test error conditions in hooks.

Capabilities

suppressErrorOutput Function

Temporarily suppresses React error boundary console output, useful for testing error scenarios without cluttering test output. Returns a function to restore normal error output.

/**
 * Suppress React error boundary console output
 * @returns Function to restore normal error output
 */
function suppressErrorOutput(): () => void;

Usage Examples:

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

function useErrorHook(shouldError: boolean) {
  const [count, setCount] = useState(0);
  
  if (shouldError) {
    throw new Error("Hook error occurred");
  }
  
  return { count, setCount };
}

test("testing hook errors with suppressed output", () => {
  // Suppress error output to keep test logs clean
  const restoreConsole = suppressErrorOutput();
  
  try {
    const { result } = renderHook(() => useErrorHook(true));
    
    // This will throw, but without console noise
    expect(result.error).toBeInstanceOf(Error);
    expect(result.error.message).toBe("Hook error occurred");
  } finally {
    // Always restore console output
    restoreConsole();
  }
});

// Using suppressErrorOutput with async tests
test("async error handling", async () => {
  const restoreConsole = suppressErrorOutput();
  
  function useAsyncError() {
    const [error, setError] = useState(null);
    
    const triggerError = async () => {
      await new Promise(resolve => setTimeout(resolve, 100));
      throw new Error("Async error");
    };
    
    return { error, triggerError };
  }
  
  try {
    const { result } = renderHook(() => useAsyncError());
    
    await expect(result.current.triggerError()).rejects.toThrow("Async error");
  } finally {
    restoreConsole();
  }
});

Error Boundary Testing

Testing hooks that throw errors requires understanding how React's error boundaries work with the testing library:

function useValidatedState(initialValue: string, validator: (value: string) => boolean) {
  const [value, setValue] = useState(initialValue);
  
  const updateValue = (newValue: string) => {
    if (!validator(newValue)) {
      throw new Error(`Invalid value: ${newValue}`);
    }
    setValue(newValue);
  };
  
  return { value, updateValue };
}

test("error boundary handling", () => {
  const restoreConsole = suppressErrorOutput();
  
  try {
    const { result } = renderHook(() => 
      useValidatedState("valid", (value) => value !== "invalid")
    );
    
    expect(result.current.value).toBe("valid");
    
    // This should throw and be caught by error boundary
    act(() => {
      expect(() => {
        result.current.updateValue("invalid");
      }).toThrow("Invalid value: invalid");
    });
    
    // Check error state
    expect(result.error).toBeInstanceOf(Error);
    expect(result.error.message).toBe("Invalid value: invalid");
  } finally {
    restoreConsole();
  }
});

Global Error Filtering

The library provides a global configuration to disable error filtering entirely:

// Import at the top of test file or in setup
import "@testing-library/react-hooks/disable-error-filtering";

// Or require in CommonJS
require("@testing-library/react-hooks/disable-error-filtering");

Usage:

// After importing disable-error-filtering
import { renderHook } from "@testing-library/react-hooks";

// All console errors will be shown, even from error boundaries
test("with error filtering disabled", () => {
  function useThrowingHook() {
    throw new Error("This error will be shown in console");
  }
  
  const { result } = renderHook(() => useThrowingHook());
  
  expect(result.error).toBeInstanceOf(Error);
  // Error will appear in console output
});

Error Recovery Testing

Testing hooks that can recover from errors:

function useErrorRecovery() {
  const [error, setError] = useState(null);
  const [retryCount, setRetryCount] = useState(0);
  const [data, setData] = useState(null);
  
  const fetchData = async () => {
    try {
      setError(null);
      
      if (retryCount < 2) {
        setRetryCount(prev => prev + 1);
        throw new Error("Temporary failure");
      }
      
      setData("Success data");
    } catch (err) {
      setError(err);
    }
  };
  
  const retry = () => {
    fetchData();
  };
  
  useEffect(() => {
    fetchData();
  }, []);
  
  return { data, error, retryCount, retry };
}

test("error recovery mechanism", async () => {
  const { result, waitFor } = renderHook(() => useErrorRecovery());
  
  // Initially should have error
  await waitFor(() => result.current.error !== null);
  
  expect(result.current.error.message).toBe("Temporary failure");
  expect(result.current.retryCount).toBe(1);
  expect(result.current.data).toBe(null);
  
  // Retry should fail again
  act(() => {
    result.current.retry();
  });
  
  await waitFor(() => result.current.retryCount === 2);
  
  expect(result.current.error.message).toBe("Temporary failure");
  expect(result.current.data).toBe(null);
  
  // Third retry should succeed
  act(() => {
    result.current.retry();
  });
  
  await waitFor(() => result.current.data !== null);
  
  expect(result.current.error).toBe(null);
  expect(result.current.data).toBe("Success data");
});

Error Context Testing

Testing hooks that provide error contexts:

const ErrorContext = React.createContext({
  error: null,
  clearError: () => {},
  reportError: (error) => {}
});

function useErrorContext() {
  return React.useContext(ErrorContext);
}

function useWithErrorReporting(operation: () => void) {
  const { reportError } = useErrorContext();
  
  const executeOperation = () => {
    try {
      operation();
    } catch (error) {
      reportError(error);
    }
  };
  
  return { executeOperation };
}

test("error context integration", () => {
  const mockReportError = jest.fn();
  const mockClearError = jest.fn();
  
  const wrapper = ({ children }) => (
    <ErrorContext.Provider value={{
      error: null,
      clearError: mockClearError,
      reportError: mockReportError
    }}>
      {children}
    </ErrorContext.Provider>
  );
  
  const { result } = renderHook(() => 
    useWithErrorReporting(() => {
      throw new Error("Operation failed");
    }),
    { wrapper }
  );
  
  act(() => {
    result.current.executeOperation();
  });
  
  expect(mockReportError).toHaveBeenCalledWith(
    expect.objectContaining({ message: "Operation failed" })
  );
});

Best Practices for Error Testing

Always Restore Console:

test("proper console restoration", () => {
  const restoreConsole = suppressErrorOutput();
  
  try {
    // Test error scenarios
    const { result } = renderHook(() => useErrorHook());
    expect(result.error).toBeDefined();
  } finally {
    // Always restore, even if test fails
    restoreConsole();
  }
});

Use Test Helpers:

// Helper function for error testing
function testHookError(hookFn, expectedError) {
  const restoreConsole = suppressErrorOutput();
  
  try {
    const { result } = renderHook(hookFn);
    expect(result.error).toBeInstanceOf(Error);
    expect(result.error.message).toBe(expectedError);
  } finally {
    restoreConsole();
  }
}

test("using error test helper", () => {
  testHookError(
    () => useValidatedState("", () => false),
    "Invalid value: "
  );
});

Error Boundaries with Cleanup:

test("error boundaries with cleanup", () => {
  const restoreConsole = suppressErrorOutput();
  const mockCleanup = jest.fn();
  
  addCleanup(mockCleanup);
  
  try {
    const { result } = renderHook(() => useErrorHook(true));
    expect(result.error).toBeDefined();
  } finally {
    restoreConsole();
  }
  
  // Cleanup should still run despite error
  expect(mockCleanup).toHaveBeenCalled();
});

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