Deck 4 - Testing: React Testing Library + Jest/Vitest (objective)
Goal: evaluate and write unit/integration tests that are robust and user-centric.
You practice: RTL queries, async flows, userEvent, mocking, MSW, and avoiding brittle tests.
Output style: 1 verdict sentence + 2-3 issues + improved test approach.
Testing philosophy (RTL).
Arrange-Act-Assert (AAA).
RTL query priority (best practice).
Prefer: getByRole (name) > getByLabelText > getByPlaceholderText > getByText > getByTestId (last resort).
getBy / queryBy / findBy (differences).
screen vs returned queries.
userEvent vs fireEvent.
Async userEvent (why await).
waitFor (when).
waitForElementToBeRemoved (when).
act (what it is).
Testing fetch flows (baseline).
Mocking fetch (baseline).
MSW (why).
Mock reset hygiene.
Fake timers (when).
Snapshot tests (rule).
Accessibility assertions (a11y).
Testing error states.
What makes a test flaky?
Gotcha: not awaiting userEvent.
test('increments', () => {
const user = userEvent.setup();
render(<Counter />);
user.click(screen.getByRole('button', { name: /inc/i }));
expect(screen.getByTestId('count')).toHaveTextContent('1');
});Verdict: FAIL
- Issue: user.click is async and not awaited.
- Impact: flaky assertion.
- Fix: await user.click(…) before expect.
Gotcha: fireEvent used for typing.
fireEvent.change(input, { target: { value: 'abc' } });Verdict: PARTIAL FAIL
- Issue: fireEvent bypasses real typing behavior.
- Fix: prefer await user.type(input, ‘abc’).
Gotcha: weak query + direct click.
screen.getByText('Submit').click();Verdict: FAIL
- Issue: weak query + direct DOM click.
- Fix: await user.click(getByRole(‘button’, { name: /submit/i })).
Gotcha: getBy used for absence.
expect(screen.getByText('Error')).not.toBeInTheDocument();Verdict: FAIL
- Issue: getBy throws when absent.
- Fix: use queryByText for negative assertions.