accelint-react-testing

Installation
SKILL.md

React Testing Best Practices

Expert guidance for writing maintainable, user-centric React component tests with Testing Library. Focused on query selection, accessibility-first testing, and avoiding implementation details.

NEVER Do When Writing React Tests

  • NEVER query by test IDs before trying accessible queries - Test IDs bypass accessibility verification: a button with data-testid="submit" but no accessible name works in tests but fails for screen reader users. When tests pass with test IDs, you ship inaccessible UIs. Query hierarchy: getByRole > getByLabelText > getByText > getByTestId. Each step down this list means less confidence your UI is usable.
  • NEVER use fireEvent for user interactions when userEvent is available - fireEvent dispatches single DOM events, missing the event sequence real users trigger: fireEvent.click() fires one click event, but real users trigger focus → mousedown → mouseup → click. Components that work with fireEvent break in production when users interact normally. userEvent.click() simulates the full interaction sequence, catching bugs fireEvent misses.
  • NEVER test implementation details instead of user behavior - Tests that verify "state variable X equals Y" or "function Z was called" create false failures: you refactor from useState to useReducer, all tests fail, yet the UI works identically. Testing implementation details punishes refactoring and provides zero confidence the user experience works. Test what users see and do (rendered output, interaction results), not how your component achieves it internally.
  • NEVER query from container or use destructured queries after initial render - const { getByText } = render(<Component />) creates stale queries that miss updates: after state changes, destructured queries search the initial DOM snapshot, missing newly rendered elements. This causes "element not found" errors for elements that are actually present. Always use screen.getByText() which automatically queries the current DOM state. Using screen consistently also makes tests more maintainable - adding a new query doesn't require updating the destructuring.
  • NEVER add aria-label or role attributes solely for tests - If you're adding aria-label="submit-button" or role="button" just so tests can find elements, you're working backwards. Tests should verify the component is already accessible, not make it accessible for tests. Adding test-only ARIA pollutes production code and masks real accessibility problems. Fix the component's semantic HTML and existing ARIA first.
  • NEVER snapshot entire component trees without specific assertions - Massive snapshots with 500+ lines break on any change (updated classname, new prop, reordered elements), forcing reviewers to approve diffs they can't meaningfully evaluate. When test failures require "just update the snapshot" without understanding why, the test has zero value. Snapshot specific critical structures (error messages, data tables) with targeted assertions for everything else.
  • NEVER use waitFor for actions that return promises - waitFor(() => expect(element).toBeInTheDocument()) polls repeatedly until timeout when a promise-based findBy query solves it in one shot: await screen.findByText('loaded') waits for the element to appear without polling. Reserve waitFor for assertions that can't use findBy (checking element disappears, waiting for attribute changes).
  • NEVER perform side effects inside waitFor callback - waitFor(() => { fireEvent.click(button); expect(text).toBeInTheDocument(); }) runs the click multiple times as waitFor retries, causing unpredictable behavior. waitFor is for waiting on assertions, not triggering actions. Perform all actions outside waitFor, then use waitFor only for the assertion: fireEvent.click(button); await waitFor(() => expect(text).toBeInTheDocument()); or better yet, await userEvent.click(button); expect(await screen.findByText(text)).toBeInTheDocument();.
  • NEVER create custom renders without documenting provider requirements - A custom renderWithRedux function with undocumented required store shape breaks for every developer: they call render(<Component />) instead of renderWithRedux(), tests fail with cryptic "Cannot read property of undefined", wasting 15 minutes debugging. Centralize provider setup in test utils with TypeScript types that enforce correct usage, or document required wrappers prominently.
  • NEVER mix queries from different Testing Library imports - Importing both @testing-library/react render and @testing-library/dom queries creates confusion: screen from react package doesn't work with getByRole from dom package, causing "screen.getByRole is not a function" errors. Import all queries from @testing-library/react for React components - it re-exports everything from dom with React-specific enhancements.

Before Writing Tests, Ask

Related skills

More from gohypergiant/agent-skills

Installs
153
GitHub Stars
13
First Seen
Feb 10, 2026