Claude Code Plugins

Community-maintained marketplace

Feedback

manage-react-hook-tests

@talbenmoshe/zdr-tools
4
0

Create or update test files for React hooks. Use when user asks to "create hook tests", "test useMyHook", "generate tests for hook", "update hook tests", or mentions needing tests for a React hook. Generates vitest test files with renderHook, Fake context providers, and proper test structure.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name manage-react-hook-tests
description Create or update test files for React hooks. Use when user asks to "create hook tests", "test useMyHook", "generate tests for hook", "update hook tests", or mentions needing tests for a React hook. Generates vitest test files with renderHook, Fake context providers, and proper test structure.

Manage React Hook Tests Skill

This skill helps you create or update test files for React hooks. Tests follow a specific pattern with vitest, renderHook from React Testing Library, and Fake context providers for controlled testing.

When to Use This Skill

Use this skill when you need to:

  • Create a new test file for a React hook
  • Update an existing test file when the hook changes
  • Generate test coverage for hook logic and return values

The skill will generate/update:

  • A test file with vitest and React Testing Library imports
  • A renderMyHook helper function using renderHook
  • Individual test cases for different hook behaviors and branches
  • Proper setup with Fake context providers when needed

Usage

Invoke this skill when the user asks to:

  • "Create tests for [useMyHook]"
  • "Generate a test file for [useMyHook]"
  • "I need tests for [useMyHook]"
  • "Update tests for [useMyHook]"
  • "The [useMyHook] changed, update its tests"
  • "Test [useMyHook]"

Core Principles

Testing Philosophy

  1. Hook-Driven: Test the hook's behavior, return values, and side effects
  2. Context Isolation: Use Fake context providers to isolate the hook from external dependencies
  3. Controlled Testing: Use Fake builders to control all dependencies and return values
  4. Single Responsibility: Each it block should test one specific scenario
  5. Avoid vi.mock for Internal: Use Fakes for internal dependencies; only use vi.mock for external libraries

Prerequisites

Before creating/updating hook tests:

  1. Verify the hook exists - The hook you're testing must already be defined
  2. Check for existing test file - Use Glob to search for existing .spec.tsx file
  3. Identify hook dependencies - Determine what contexts or other hooks it uses
  4. Locate Fake providers - Find available Fake context providers (usually in the same package under /fakes)
  5. Check for @testing-library/react - Verify that @testing-library/react is installed (for renderHook)

Create vs Update Decision

If test file exists: Update mode

  • Read the existing test file
  • Read the hook definition
  • Compare and identify what's missing or outdated
  • Update the test to match current implementation

If test file does NOT exist: Create mode

  • Read the hook definition
  • Identify all dependencies and context requirements
  • Generate complete test file from scratch

Test File Location

CRITICAL: Test files MUST be in the __tests__ folder, which is a SIBLING of the /src folder, NOT inside it.

Directory Structure

packages/
  my-package/
    src/
      hooks/
        useMyHook.ts
    __tests__/              # Sibling to src/, NOT inside src/
      useMyHook.spec.tsx    # .tsx extension for React components

Naming Convention

For a hook named useMyHook:

  • Test file: useMyHook.spec.tsx (matches hook name exactly, with .tsx extension)
  • Located in: packages/my-package/__tests__/useMyHook.spec.tsx

Test File Structure

1. Imports

Import Rules:

  • Import React (required for this project's JSX transform)
  • Import vitest utilities from 'vitest'
  • Import renderHook (and act if needed) from '@testing-library/react'
  • Import the hook being tested
  • Import Fake context providers from /fakes subpath
  • Import Fake builders for services/dependencies when needed

Example:

import React from 'react';
import { describe, it, expect, vi } from 'vitest';
import { renderHook, act } from '@testing-library/react';
import { useMyHook } from '../src/hooks/useMyHook';
import { FakeMyContextProvider } from '../fakes';
import { FakeServiceBuilder } from '@someScope/some-library/fakes';
import type { IService } from '@someScope/some-library';

2. Main Describe Block

describe('useMyHook', () => {
  // Helper function
  function renderMyHook(/* overrides */): { /* return type */ } {
    // ...
  }

  // Test cases
  it('should return initial state', () => {
    // Test...
  });
});

Structure Rules:

  • Main describe uses the hook name (e.g., 'useMyHook')
  • Contains one renderMyHook helper function at the top
  • All test cases use the renderMyHook helper
  • Tests cover all branches and return values

3. renderMyHook Helper Function

Purpose: Factory function that renders the hook with all necessary context providers and dependencies, allowing for easy overrides in tests.

Pattern with Context:

function renderMyHook(overrides?: {
  service?: IService;
}) {
  const service = overrides?.service ?? new FakeServiceBuilder().build();

  const hookRenderResult = renderHook(() => useMyHook(), {
    wrapper: ({ children }) => (
      <FakeMyContextProvider service={service}>
        {children}
      </FakeMyContextProvider>
    ),
  });

  return {
    hookRenderResult,
    service,
  };
}

Pattern without Context:

function renderMyHook(params?: {
  initialValue?: string;
}) {
  const hookRenderResult = renderHook(() => useMyHook(params?.initialValue ?? 'default'));

  return {
    hookRenderResult,
  };
}

Rules:

  • Accept overrides parameter for context dependencies
  • Accept params parameter for hook arguments
  • Use nullish coalescing (??) for default Fake instances
  • Use renderHook with wrapper option for context providers
  • Return object with hookRenderResult and all dependencies

4. Finding Context Dependencies

How to Identify:

  1. Read the hook file and look for useContext calls or other context hooks
  2. Search for the context provider in the codebase
  3. Check if a Fake version exists in /fakes folder
  4. If no Fake exists, create one or use vi.mock for external dependencies

Multiple Contexts: Nest providers in the wrapper:

wrapper: ({ children }) => (
  <FakeContextA>
    <FakeContextB>
      {children}
    </FakeContextB>
  </FakeContextA>
)

5. Writing Test Cases

Structure:

  • Use descriptive test names explaining the expected behavior
  • Follow Arrange-Act-Assert pattern
  • Access hook result via hookRenderResult.result.current
  • Use act() for state updates
  • Use await act(async () => ...) for async operations

Example:

it('should return expected value', () => {
  const { hookRenderResult } = renderMyHook({
    service: new FakeServiceBuilder()
      .withGetDataReturnValue({ id: '123' })
      .build()
  });

  expect(hookRenderResult.result.current.data).toEqual({ id: '123' });
});

6. Testing State Updates

Always wrap state changes in act():

it('should update state when action is called', () => {
  const { hookRenderResult } = renderMyHook();

  act(() => {
    hookRenderResult.result.current.updateValue('new value');
  });

  expect(hookRenderResult.result.current.value).toBe('new value');
});

7. Testing Async Operations

Use await act(async () => ...):

it('should save successfully', async () => {
  const { hookRenderResult } = renderMyHook();

  await act(async () => {
    await hookRenderResult.result.current.save();
  });

  expect(hookRenderResult.result.current.isSaving).toBe(false);
});

8. Mocking Dependencies

Preferred (Fake Providers):

const { hookRenderResult } = renderMyHook({
  service: new FakeServiceBuilder()
    .withFetchDataReturnValue(Promise.resolve({ success: true }))
    .build()
});

When to use vi.mock:

  • External React hooks from 3rd party libraries
  • Browser APIs
  • Modules without Fake implementations

Workflow

Create Workflow

  1. Identify the hook - Determine which hook to test
  2. Locate the hook file - Use Glob to find the hook definition
  3. Read the hook - Understand behavior, return values, context usage
  4. Identify dependencies - Look for context hooks and other dependencies
  5. Locate Fakes - Find Fake providers and builders
  6. Ensure __tests__ exists - Create folder if needed (sibling to /src)
  7. Verify @testing-library/react - Check package.json
  8. Create test file in __tests__/useMyHook.spec.tsx with:
    • All required imports (including React)
    • Main describe block
    • renderMyHook helper function
    • Test cases covering all scenarios

Update Workflow

  1. Read existing test and hook - Compare current state
  2. Identify changes:
    • New return values → Add test cases
    • Changed logic → Update test cases
    • New dependencies → Update renderMyHook and imports
    • Removed functionality → Remove tests
  3. Apply updates using Edit tool - Targeted changes only
  4. Verify coverage - Ensure all logic branches tested

Update Guidelines

  • Preserve structure - Use Edit tool, not Write
  • Maintain consistency - Follow existing patterns
  • Keep descriptive names - Clear, behavior-focused
  • Don't delete passing tests - Only update broken/obsolete tests
  • Add missing coverage - Test new logic

Best Practices

  1. Test Behavior, Not Implementation - Focus on return values and side effects
  2. Descriptive Test Names - Explain expected behavior clearly
  3. Arrange-Act-Assert - Structure tests consistently
  4. Use act() for Updates - Always wrap state changes
  5. Test Edge Cases - Null, undefined, empty values, errors
  6. Appropriate Matchers - Use correct expect matchers for the assertion

Common Pitfalls to Avoid

  1. Don't Render Hook Directly - Always use renderHook()
  2. Don't Forget React Import - Required for JSX in this project
  3. Don't Forget act() - State updates need to be wrapped
  4. Don't Use vi.mock for Internal - Use Fake providers instead
  5. Don't Forget async/await - Async operations need proper handling

Example Reference

See examples.md in the same directory for complete working examples of:

  • Simple hooks returning context values
  • Hooks with complex logic and multiple return values
  • Hooks with parameters
  • Hooks with state management
  • Standalone hooks without context
  • Hooks using multiple contexts

Important Notes

File Organization

  • Tests in __tests__/ at package root (sibling to /src)
  • Use .tsx extension
  • Match hook name exactly

Dependencies

  • Ensure @testing-library/react is installed
  • Use Fake providers from /fakes subpath
  • Create Fakes if they don't exist

Test Quality

  • Many small tests > few large tests
  • Test happy path and error cases
  • Test edge cases and boundaries
  • Descriptive test names
  • Simple, focused tests

Running Tests

After creating/updating:

  1. Run pnpm test to verify tests pass
  2. Run pnpm lint to check linting
  3. Run pnpm build to verify TypeScript compiles