Avoid data-testid for assertions. Use semantic and accessible selectors instead.
Preference order:
- Accessible queries:
getByRole,getByLabelText,getByPlaceholderText,getByText - Semantic HTML and ARIA attributes
- User-centric selectors over implementation details
// ❌ Avoid
const button = screen.getByTestId("submit-button");
// ✅ Prefer
const button = screen.getByRole("button", { name: /submit/i });Analyze existing test patterns before writing new tests. Maintain consistency by:
- Reviewing similar test files in the module
- Matching naming conventions and assertion styles
- Following established setup/teardown patterns
- Using consistent mocking strategies
Ensure all test files are error-free:
- ✅ No TypeScript errors
- ✅ No linting violations
- ✅ Proper formatting
- ✅ Type-safe mocks and assertions
npm run type-check && npm run lint && npm run formatTarget 100% code coverage while keeping tests lean. Stop adding tests once full coverage is achieved. Focus on meaningful cases, not redundant ones.
Coverage goals (flexible): Strive for high coverage, but prioritize test quality over arbitrary thresholds.
npm run test:coverageNote: While high coverage is valuable, it's not a strict requirement. Write tests that add value and verify important functionality rather than targeting specific coverage percentages.
Use descriptive test names that explain expected behavior:
it('should disable submit button when form is invalid')- Follow Arrange-Act-Assert (AAA) pattern
- Group related tests with
describeblocks - Use Given-When-Then for complex scenarios
Handle asynchronous operations properly:
- Use
waitForandfindBy*queries for async operations - Avoid arbitrary timeouts (
setTimeout) - use proper async utilities - Test loading and error states explicitly
- Clean up async operations in
afterEachhooks
// ✅ Good
const element = await screen.findByRole("button", { name: /submit/i });
await waitFor(() => expect(mockFn).toHaveBeenCalled());
// ❌ Avoid
await new Promise((resolve) => setTimeout(resolve, 1000));Keep test suites fast and efficient:
- Aim for <1 second per test file
- Avoid unnecessary renders or component mounting
- Use
screen.debug()sparingly (debugging only) - Consider test parallelization for large suites
Write lean, clean, and consistent tests that prioritize user experience, maintain code quality, achieve comprehensive coverage, and run fast.