| name | manage-react-component-tests |
| description | 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. |
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