| name | frontend/typescript-testing |
| description | Frontend testing rules with Jest, React Testing Library, and MSW. Includes coverage requirements, test design principles, and quality criteria. |
TypeScript Testing Rules (Frontend)
Test Framework
- Jest: This project uses Jest
- React Testing Library: For component testing
- MSW (Mock Service Worker): For API mocking
- Test imports:
import { describe, it, expect, beforeEach, jest } from '@jest/globals'
- Component test imports:
import { render, screen, fireEvent } from '@testing-library/react'
- Mock creation: Use
jest.mock()
Basic Testing Policy
Quality Requirements
- Coverage: Unit test coverage must be 60% or higher (MANDATORY - Frontend standard 2025)
- Independence: Each test can run independently without depending on other tests
- Reproducibility: Tests are environment-independent and always return the same results
- Readability: Test code maintains the same quality as production code
Coverage Requirements (ADR-0002 Compliant)
Mandatory: Unit test coverage must be 60% or higher
Component-specific targets:
- Atoms (Button, Text, etc.): 70% or higher
- Molecules (FormField, etc.): 65% or higher
- Organisms (Header, Footer, etc.): 60% or higher
- Custom Hooks: 65% or higher (MUST include infinite loop and update cycle tests)
- State Stores (Zustand, Context): 65% or higher (MUST include update cycle and memory leak tests)
- Utils: 70% or higher
Metrics: Statements, Branches, Functions, Lines
Test Types and Scope
-
Unit Tests (React Testing Library) (MANDATORY)
- Verify behavior of individual components or functions
- Mock all external dependencies
- Most numerous, implemented with fine granularity
- Focus on user-observable behavior
- Implementation is required
-
Integration Tests (React Testing Library + MSW) (Specification MANDATORY, Implementation OPTIONAL)
- Verify coordination between multiple components
- Mock APIs with MSW (Mock Service Worker)
- No actual DB connections (backend manages DB)
- Verify major functional flows
- Test specifications (it.todo()) are mandatory
- Test implementation is optional but recommended for high-ROI tests
-
E2E Tests (Specification MANDATORY, Implementation OPTIONAL)
- Mandatory verification of impact on existing features when adding new features
- Cover integration points with "High" and "Medium" impact levels from Design Doc's "Integration Point Map"
- Verification pattern: Existing feature operation -> Enable new feature -> Verify continuity of existing features
- Success criteria: No change in displayed content, rendering time within 5 seconds
- Designed for automatic execution in CI/CD pipelines
- Test specifications (it.todo()) are mandatory
- Test implementation is optional but recommended for critical user journeys
Test Specification Requirements
For Integration and E2E tests, test specifications (using it.todo()) are mandatory and must include:
Required Metadata in Comments
it.todo('AC[N]: [Clear test description]');
Purpose
- Traceability: Link tests back to Design Doc acceptance criteria
- Prioritization: ROI scores help prioritize which tests to implement
- Planning: Complexity and dependency metadata guide implementation
- Documentation: Future developers understand test intent
Implementation Policy
- Unit test implementation: Mandatory (must pass, cannot use it.todo())
- Integration/E2E test specification: Mandatory (it.todo() with full metadata)
- Integration/E2E test implementation: Optional (based on ROI, resources, timeline)
Test Implementation Conventions
Directory Structure (Co-location Principle)
src/
└── components/
└── Button/
├── Button.tsx
├── Button.test.tsx # Co-located with component
└── index.ts
Rationale:
- React Testing Library best practice
- ADR-0002 Co-location principle
- Easy to find and maintain tests alongside implementation
Naming Conventions
- Test files:
{ComponentName}.test.tsx
- Integration test files:
{FeatureName}.integration.test.tsx
- E2E test files:
{feature-name}.e2e.test.tsx
- Test suites: Names describing target components or features
- Test cases: Names describing expected behavior from user perspective
Test Code Quality Rules
Recommended: Keep all tests always active
- Merit: Guarantees test suite completeness
- Practice: Fix problematic tests and activate them
Avoid: test.skip() or commenting out
- Reason: Creates test gaps and incomplete quality checks
- Solution: Completely delete unnecessary tests
Allowed: it.todo() for Integration/E2E tests
- Merit: Documents test intent and requirements
- Practice: Include full metadata in comments
- Requirement: Mandatory for all planned Integration/E2E tests
Custom Hooks and Stores Testing Requirements
MANDATORY: Infinite Loop and Update Cycle Detection
All custom hooks and state management stores (Zustand, Context, etc.) MUST be tested for:
1. Infinite Re-render Prevention
- Hooks don't trigger infinite re-render loops
- Dependency arrays are correct and stable
- useEffect/useCallback/useMemo dependencies don't cause cascading updates
- Maximum render count validation (should not exceed 2-3 renders for simple updates)
2. Update Cycle Validation
- State updates complete within reasonable cycles (max 3-5 updates)
- Derived state calculations don't trigger circular dependencies
- Selector functions are properly memoized (store subscriptions trigger only once per base state change)
3. Memory Leak Prevention
- Subscriptions properly cleaned up on unmount
- Event listeners removed on unmount
- Timers and intervals cleared on unmount
Testing Approach: Use renderHook from @testing-library/react with render counters, subscription spies, and cleanup verification.
Mock Type Safety Enforcement
MSW (Mock Service Worker) Setup
import { rest } from 'msw';
const handlers = [
rest.get('/api/users/:id', (req, res, ctx) => {
return res(ctx.json({ id: '1', name: 'John' } satisfies User));
}),
];
Component Mock Type Safety
type TestProps = Pick<ButtonProps, 'label' | 'onClick'>;
const mockProps: TestProps = { label: 'Click', onClick: jest.fn() };
const mockRouter = {
push: jest.fn(),
} as unknown as Router;
Basic React Testing Library Example
import { describe, it, expect, jest } from '@jest/globals';
import { render, screen, fireEvent } from '@testing-library/react';
import { Button } from './Button';
describe('Button', () => {
it('should call onClick when clicked', () => {
const onClick = jest.fn();
render(<Button label="Click me" onClick={onClick} />);
fireEvent.click(screen.getByRole('button', { name: 'Click me' }));
expect(onClick).toHaveBeenCalledOnce();
});
});
Integration Test Specification Example
import { describe, it } from '@jest/globals';
describe('Checkout Flow Integration Test', () => {
it.todo('AC2: Complete checkout flow with saved payment method shows confirmation');
});