Universal testing philosophy and best practices.
Core philosophy:
- Test behavior, not implementation details
- Query elements like users do (semantic first)
- Accessibility-first approach
- One test per behavior
Query priority:
- getByRole - What assistive technologies see ✅
- getByLabelText - Form labels ✅
- getByText - Visible content ✅
- getByTestId - Last resort ⚠️
Coverage:
- Pure utilities: 100% coverage
- UI components: User-facing behavior
- Always test happy path + error states
Test behavior, not implementation details.
Focus on:
- ✅ What the user sees and does
- ✅ How users interact with the application
- ✅ Accessibility for all users
Avoid:
- ❌ Internal component state
- ❌ CSS selectors or class names
- ❌ Component structure or hierarchy
1. Role-Based Queries (Most Preferred)
Query by ARIA roles - what assistive technologies see:
Common ARIA roles:
- Interactive:
button, link, checkbox, radio, textbox, combobox
- Structural:
heading, navigation, main, banner
- Widgets:
dialog, alert, tab, tabpanel, progressbar
- Lists/Tables:
list, listitem, table, row, cell
Query form inputs by labels:
Query by visible text content:
4. Test IDs (Last Resort)
Only use when semantic queries aren't possible:
Accessibility-First Testing
Arrange-Act-Assert Pattern
100% coverage for:
- Pure utility functions
- All custom hooks
- Business logic
Behavior coverage for:
- UI components (not implementation)
- At minimum: 1 smoke test + 1 behavior test per component
Always test:
- Happy path (success scenarios)
- Error states (failure scenarios)
- Edge cases (boundary conditions)
- Accessibility (keyboard nav, ARIA, focus)
Don't Query by CSS Selectors
Don't Test Implementation Details
Don't Test Too Much in One Test
Target durations:
- Unit tests: <100ms per test
- Integration tests: <10-15s per test
- E2E tests: <60s per test
Flakiness management:
See Unit Testing for RTL-specific patterns.
See Playwright documentation for E2E testing patterns.
For more detailed technical guidance, see these reference files: