---
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 }) => (
{children}
),
});
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 }) => (
{children}
)
```
### 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