manage-react-hook-tests
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.
- Category: discovered
- Tags: github, community
- Version: 1.0.0
- Author: walid
- Downloads: 0
- GitHub stars: 4
- Updated: 2025-11-21
Download the latest version (v1.0.0) from this page.
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:**
```typescript
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
```typescript
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:**
```typescript
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:**
```typescript
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:
```typescript
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:**
```typescript
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()`:
```typescript
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 () => ...)`:
```typescript
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):**
```typescript
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