Review & Cheat Sheet: Testing Strategies
[!NOTE] This module explores the core principles of Review & Cheat Sheet: Testing Strategies, deriving solutions from first principles and hardware constraints to build world-class, production-ready expertise.
1. Key Takeaways
- Test Behavior, Not Implementation: Avoid testing internal state (props, state). Test what the user sees (text, buttons).
- The Testing Trophy: Focus heavily on Integration Tests using React Testing Library (RTL). They give the best return on investment (ROI).
- Query Priority: Always prefer
getByRolefor accessibility. UsegetByTestIdonly as a last resort. - Async Testing: Use
findBy...orwaitForfor elements that appear after an API call. - E2E for Critical Paths: Use Playwright for “happy path” user flows (Login → Checkout). Use auto-waiting to avoid flakiness.
- Mock Network, Not Client: Use MSW to intercept requests at the network layer. This tests your actual data fetching logic.
- Global Setup: Use Playwright’s global setup to handle authentication once and reuse state across tests.
2. Interactive Flashcards
Question
Answer
3. Cheat Sheet: RTL Queries
| Type | Single Element | Multiple Elements | Async (Wait)? | Returns if Missing | Use Case |
|---|---|---|---|---|---|
| Get | getBy... |
getAllBy... |
No | Error | Standard assertion |
| Query | queryBy... |
queryAllBy... |
No | null |
Assert absence (not.toBeInTheDocument) |
| Find | findBy... |
findAllBy... |
Yes | Error (timeout) | Async elements (API data) |
Common Matchers (jest-dom)
expect(element).toBeInTheDocument();
expect(element).toBeVisible();
expect(element).toHaveTextContent(/hello/i);
expect(element).toHaveAttribute('src', 'logo.png');
expect(element).toBeDisabled();
expect(input).toHaveValue('user input');
4. Anatomy of an RTL Test (The AAA Pattern)
Testing UI components typically follows the Arrange, Act, Assert (AAA) pattern:
test('submits form and displays success message', async () => {
// 1. Arrange: Render the component and set up initial state/mocks
const user = userEvent.setup();
render(<ContactForm />);
// 2. Act: Simulate user interactions
await user.type(screen.getByRole('textbox', { name: /email/i }), 'test@example.com');
await user.click(screen.getByRole('button', { name: /submit/i }));
// 3. Assert: Verify the expected outcome on the screen
expect(await screen.findByText(/success/i)).toBeInTheDocument();
});
5. Mnemonics: The RTL Query Family
To remember when to use which query type, think of them as different types of workers:
getBy...(The Bouncer): Strict. Expects the element to be there right now. If it’s missing, or if there’s more than one, the Bouncer throws an error and stops the show immediately.queryBy...(The Receptionist): Polite. You ask, “Is element X here?” If yes, they hand it over. If no, they calmly returnnull. Perfect for verifying that an error message isn’t on the screen.findBy...(The Detective): Patient. You say, “I know element X will show up eventually.” The Detective waits (returns a Promise) until the element appears or until the timeout is reached. Use for anything appearing after an API call or timer.
6. Industry War Story: The Flaky Test Epidemic
Scenario: A large e-commerce team had an integration test suite that failed randomly 15% of the time in CI, blocking releases.
The Culprit: The tests used fireEvent.change() combined with hardcoded setTimeout(..., 500) to wait for debounced search results. Depending on CI server load, 500ms was sometimes too fast, causing random failures.
The Fix:
- Replaced
fireEventwithuserEvent.type(), which accurately simulates the delays between real keystrokes. - Replaced the hardcoded
setTimeoutwithawait screen.findByRole('listitem'). Result: RTL’sfindByautomatically retries the assertion until it passes or times out (default 1000ms), making the test 100% resilient to CPU load variations without artificially slowing down fast runs.
7. Quick Revision
- Unit Tests: Test logic in isolation.
- Integration Tests: Test components working together (RTL). Focus here.
- E2E Tests: Test full user flows (Playwright).
- Static Analysis: ESLint/TypeScript catch typos.