Agent skill
manage-react-component-tests
Create or update test files for React components. Use when user asks to "create component tests", "test MyComponent", "generate tests for component", "update component tests", or mentions needing tests for a React component. Generates vitest test files with render, mocked sub-components, and proper test structure focusing on logic and prop validation.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/testing/manage-react-component-tests
SKILL.md
Manage React Component Tests Skill
This skill helps you create or update test files for React components. Tests follow a specific pattern with vitest, render from React Testing Library, and mocked sub-components to test logic and prop passing in isolation.
When to Use This Skill
Use this skill when you need to:
- Create a new test file for a React component
- Update an existing test file when the component changes
- Generate test coverage for component logic and prop validation
The skill will generate/update:
- A test file with vitest and React Testing Library imports
- Mocked versions of all imported sub-components
- A
renderMyComponenthelper function usingrender - Individual test cases for different component behaviors and prop permutations
Usage
Invoke this skill when the user asks to:
- "Create tests for [MyComponent]"
- "Generate a test file for [MyComponent]"
- "I need tests for [MyComponent]"
- "Update tests for [MyComponent]"
- "The [MyComponent] changed, update its tests"
- "Test [MyComponent]"
Core Principles
Testing Philosophy
- Logic-Focused: Test component logic, not UI appearance
- Prop Validation: Verify sub-components receive correct props
- Isolation: Mock all sub-components to test the component in isolation
- Callback Testing: Simulate callbacks to test handlers
- Unit Testing: Each component has its own tests; dependencies should have separate tests
Prerequisites
Before creating/updating component tests:
- Verify the component exists - The component you're testing must already be defined
- Check for existing test file - Use Glob to search for existing
.spec.tsxfile - Identify sub-components - Determine what components are imported and rendered
- Verify @testing-library/react - Ensure it's installed (for
render)
Create vs Update Decision
If test file exists: Update mode
- Read the existing test file
- Read the component 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 component definition
- Identify all sub-components to mock
- 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/
components/
MyComponent.tsx
__tests__/ # Sibling to src/, NOT inside src/
MyComponent.spec.tsx # .tsx extension for React components
Naming Convention
For a component named MyComponent:
- Test file:
MyComponent.spec.tsx(matches component name exactly, with.tsxextension) - Located in:
packages/my-package/__tests__/MyComponent.spec.tsx
Test File Structure
1. Imports
Import Rules:
- Import React (required for this project's JSX transform)
- Import vitest utilities from
'vitest' - Import
renderandscreenfrom'@testing-library/react' - Import the component being tested
- DO NOT import sub-components (they will be mocked)
Example:
import React from 'react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen } from '@testing-library/react';
import { MyComponent } from '../src/components/MyComponent';
2. Mocking Sub-Components
All imported sub-components must be mocked to test the component in isolation.
Pattern:
// Mock all sub-component modules
vi.mock('../src/components/SubComponentA', () => ({
SubComponentA: vi.fn(() => <div data-testid="mock-sub-component-a">SubComponentA</div>)
}));
vi.mock('../src/components/SubComponentB', () => ({
SubComponentB: vi.fn(() => <div data-testid="mock-sub-component-b">SubComponentB</div>)
}));
Rules:
- Place mocks at the top of the file, after imports
- Use
vi.fn()to create a mock function that returns a simple<div> - Add
data-testidfor easy querying in tests - Mock EVERY component imported by the component under test
3. Main Describe Block
describe('MyComponent', () => {
// Helper function
function renderMyComponent(props?: Partial<MyComponentProps>) {
// ...
}
// Reset mocks before each test
beforeEach(() => {
vi.clearAllMocks();
});
// Test cases
it('should render sub-components with correct props', () => {
// Test...
});
});
Structure Rules:
- Main describe uses the component name
- Contains one
renderMyComponenthelper function at the top - Includes
beforeEachto clear mocks - All test cases use the
renderMyComponenthelper
4. renderMyComponent Helper Function
Purpose: Factory function that renders the component with default or custom props.
Pattern:
function renderMyComponent(props?: Partial<MyComponentProps>) {
const defaultProps: MyComponentProps = {
title: 'Test Title',
onSubmit: vi.fn(),
items: [],
};
return render(<MyComponent {...defaultProps} {...props} />);
}
Rules:
- Accept partial props (optional)
- Define sensible defaults for all required props
- Use
vi.fn()for callback props - Spread defaults first, then custom props
- Return the result of
render()
5. Testing Sub-Component Props
Access the mock to verify props:
import { SubComponentA } from '../src/components/SubComponentA';
it('should pass correct props to SubComponentA', () => {
renderMyComponent({ title: 'My Title', count: 5 });
expect(SubComponentA).toHaveBeenCalledWith(
expect.objectContaining({
title: 'My Title',
count: 5
}),
expect.anything() // React context
);
});
Rules:
- Import the mocked component at the top
- Use
expect(MockedComponent).toHaveBeenCalledWith() - Use
expect.objectContaining()to match props - Use
expect.anything()as second arg (React context)
6. Testing Callbacks
Simulate callback invocation to test handlers:
it('should call onSubmit when button is clicked', () => {
const mockOnSubmit = vi.fn();
const { SubButton } = require('../src/components/SubButton');
renderMyComponent({ onSubmit: mockOnSubmit });
// Get the onClick prop passed to SubButton
const onClickProp = SubButton.mock.calls[0][0].onClick;
// Simulate the click
onClickProp();
expect(mockOnSubmit).toHaveBeenCalled();
});
Rules:
- Pass
vi.fn()as callback props - Extract callback from mock's call arguments
- Invoke the callback to test the handler
- Assert the handler was called correctly
7. Testing Conditional Rendering
Test different branches:
it('should render ErrorMessage when error prop is provided', () => {
const { ErrorMessage } = require('../src/components/ErrorMessage');
renderMyComponent({ error: 'Something went wrong' });
expect(ErrorMessage).toHaveBeenCalledWith(
expect.objectContaining({
message: 'Something went wrong'
}),
expect.anything()
);
});
it('should not render ErrorMessage when no error', () => {
const { ErrorMessage } = require('../src/components/ErrorMessage');
renderMyComponent({ error: null });
expect(ErrorMessage).not.toHaveBeenCalled();
});
8. Testing Lists and Iterations
Test components rendered in loops:
it('should render ListItem for each item', () => {
const { ListItem } = require('../src/components/ListItem');
const items = [
{ id: '1', name: 'Item 1' },
{ id: '2', name: 'Item 2' },
{ id: '3', name: 'Item 3' }
];
renderMyComponent({ items });
expect(ListItem).toHaveBeenCalledTimes(3);
expect(ListItem).toHaveBeenNthCalledWith(
1,
expect.objectContaining({ id: '1', name: 'Item 1' }),
expect.anything()
);
expect(ListItem).toHaveBeenNthCalledWith(
2,
expect.objectContaining({ id: '2', name: 'Item 2' }),
expect.anything()
);
expect(ListItem).toHaveBeenNthCalledWith(
3,
expect.objectContaining({ id: '3', name: 'Item 3' }),
expect.anything()
);
});
Workflow
Create Workflow
- Identify the component - Determine which component to test
- Locate the component file - Use Glob to find the component definition
- Read the component - Understand props, sub-components, logic branches
- Identify sub-components - List all imported components to mock
- Ensure
__tests__exists - Create folder if needed (sibling to/src) - Verify @testing-library/react - Check package.json
- Create test file in
__tests__/MyComponent.spec.tsxwith:- All required imports (including React)
- Mock declarations for all sub-components
- Main describe block
renderMyComponenthelper functionbeforeEachto clear mocks- Test cases covering all scenarios
Update Workflow
- Read existing test and component - Compare current state
- Identify changes:
- New sub-components → Add mocks
- Changed props → Update test cases
- New logic branches → Add test cases
- Removed functionality → Remove tests
- Apply updates using Edit tool - Targeted changes only
- 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 and props
Best Practices
- Test Logic, Not UI - Focus on props passed and callbacks invoked
- Mock All Sub-Components - Test the component in complete isolation
- Descriptive Test Names - Explain expected behavior clearly
- Clear Mocks Between Tests - Use
beforeEachwithvi.clearAllMocks() - Test All Branches - Cover conditional rendering, loops, error states
- Test Callbacks - Simulate sub-component callbacks to test handlers
- Use expect.objectContaining - Match specific props without over-specifying
Common Pitfalls to Avoid
- ❌ Don't Import Sub-Components Normally - They should be mocked, not imported
- ❌ Don't Forget React Import - Required for JSX in this project
- ❌ Don't Forget beforeEach - Mocks persist between tests
- ❌ Don't Test UI Appearance - Focus on logic and prop passing
- ❌ Don't Skip expect.anything() - It's needed as the second argument for React context
- ❌ Don't Forget to Mock Everything - ALL sub-components must be mocked
Example Reference
See examples.md in the same directory for complete working examples of:
- Simple component with sub-components
- Component with conditional rendering
- Component with lists and iterations
- Component with callbacks and handlers
- Component with complex prop passing
- Component with multiple branches
Important Notes
File Organization
- Tests in
__tests__/at package root (sibling to/src) - Use
.tsxextension - Match component name exactly
Dependencies
- Ensure
@testing-library/reactis installed - Use
vi.mock()for all sub-components - Mock at the module level, not inside tests
Test Quality
- Many small tests > few large tests
- Test happy path and error cases
- Test all conditional branches
- Descriptive test names
- Simple, focused tests
Running Tests
After creating/updating:
- Run
pnpm testto verify tests pass - Run
pnpm lintto check linting - Run
pnpm buildto verify TypeScript compiles
Mocking Best Practices
- Always use
vi.fn()for mock components - Return simple
<div>elements withdata-testid - Clear mocks in
beforeEach - Import mocked components when you need to assert on them
- Use
expect.objectContaining()to verify props
Didn't find tool you were looking for?