Deck 4 - Testing: React Testing Library + Jest/Vitest Flashcards

RTL queries + async testing + userEvent + mocking/MSW + anti-flake patterns. (80 cards)

1
Q

Deck 4 - Testing: React Testing Library + Jest/Vitest (objective)

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Testing philosophy (RTL).

A
  • Test behavior, not implementation details.
  • Prefer user-visible output and accessibility roles.
  • Avoid testing internals (state/private functions).
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Arrange-Act-Assert (AAA).

A
  • Arrange: render + setup.
  • Act: user interactions.
  • Assert: expectations.
  • Keep steps minimal and obvious.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

RTL query priority (best practice).

A

Prefer: getByRole (name) > getByLabelText > getByPlaceholderText > getByText > getByTestId (last resort).

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

getBy / queryBy / findBy (differences).

A
  • getBy*: throws if not found (sync).
  • queryBy*: returns null if not found (sync).
  • findBy*: returns Promise and waits (async).
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

screen vs returned queries.

A
  • Prefer screen.* for consistency.
  • Use render() return only when needed (rerender/container).
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

userEvent vs fireEvent.

A
  • userEvent simulates real user interactions (recommended).
  • fireEvent is lower-level (use sparingly).
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Async userEvent (why await).

A
  • userEvent actions are async.
  • await user.click/type to flush events.
  • Prevent flaky tests.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

waitFor (when).

A
  • Use to wait for async state updates.
  • Prefer findBy* for waiting on an element.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

waitForElementToBeRemoved (when).

A
  • Use when UI disappears (e.g., loading).
  • Avoid manual timeouts.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

act (what it is).

A
  • Ensures React updates are flushed before assertions.
  • RTL + userEvent cover most cases; use act for edge cases.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Testing fetch flows (baseline).

A
  • Mock network or use MSW.
  • Assert loading -> success/error.
  • Avoid real network calls.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Mocking fetch (baseline).

A
  • Stub global.fetch and restore/reset between tests.
  • Ensure mocked responses include ok/status/json.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

MSW (why).

A
  • Intercepts requests at the network layer.
  • More realistic than mocking fetch.
  • Encourages integration-style tests.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Mock reset hygiene.

A
  • Reset mocks between tests.
  • Avoid cross-test pollution.
  • Use beforeEach/afterEach.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Fake timers (when).

A
  • For debounce/timeout/interval behavior.
  • Advance timers and restore real timers.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

Snapshot tests (rule).

A
  • Use sparingly.
  • Large snapshots are brittle.
  • Prefer targeted assertions.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

Accessibility assertions (a11y).

A
  • getByRole + name doubles as a11y check.
  • Prefer role=alert for errors.
  • Avoid testing ARIA incorrectly.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

Testing error states.

A
  • Assert visible error UI.
  • Clear error on retry.
  • Do not swallow errors silently.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

What makes a test flaky?

A
  • Missing awaits, timing assumptions, shared state leakage, asserting before DOM settles.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q

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');
});
A

Verdict: FAIL
- Issue: user.click is async and not awaited.
- Impact: flaky assertion.
- Fix: await user.click(…) before expect.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q

Gotcha: fireEvent used for typing.

fireEvent.change(input, { target: { value: 'abc' } });
A

Verdict: PARTIAL FAIL
- Issue: fireEvent bypasses real typing behavior.
- Fix: prefer await user.type(input, ‘abc’).

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
23
Q

Gotcha: weak query + direct click.

screen.getByText('Submit').click();
A

Verdict: FAIL
- Issue: weak query + direct DOM click.
- Fix: await user.click(getByRole(‘button’, { name: /submit/i })).

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Q

Gotcha: getBy used for absence.

expect(screen.getByText('Error')).not.toBeInTheDocument();
A

Verdict: FAIL
- Issue: getBy throws when absent.
- Fix: use queryByText for negative assertions.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
25
Gotcha: missing mock reset. ``` global.fetch = vi.fn(); // no reset between tests ```
Verdict: FAIL - Issue: mock state leaks. - Fix: restore/reset in afterEach (vi.restoreAllMocks()).
26
Gotcha: brittle snapshot. ``` expect(container).toMatchSnapshot(); ```
Verdict: PARTIAL FAIL - Issue: large snapshots are brittle. - Fix: assert key behavior/text/roles.
27
Gotcha: assertion before async settles. ``` render(); expect(screen.getByText('Alice')).toBeInTheDocument(); ```
Verdict: FAIL - Issue: async data not awaited. - Fix: await screen.findByText('Alice') (or waitFor).
28
Gotcha: waitFor without assertion. ``` await waitFor(() => doSomething()); ```
Verdict: FAIL - Issue: waitFor should wrap an assertion. - Fix: put expect(...) inside waitFor.
29
Gotcha: waitFor around getBy (better is findBy). ``` await waitFor(() => screen.getByText('Done')); ```
Verdict: PARTIAL - Fix: use await screen.findByText('Done').
30
Gotcha: implementation detail selector. ``` expect(container.querySelector('.btn-primary')).toBeTruthy(); ```
Verdict: FAIL - Issue: tests styling/structure. - Fix: assert by role/name and outcome.
31
Gotcha: overusing data-testid. ``` screen.getByTestId('submit'); ```
Verdict: PARTIAL FAIL - Issue: role/name is preferred. - Fix: use getByRole('button', { name: ... }) when possible.
32
Gotcha: error UI not accessible. ``` return
Error: failed
; ```
Verdict: PARTIAL FAIL - Issue: no role=alert. - Fix:
... and query by role.
33
Gotcha: testing internals. ``` expect(componentInstance.state.count).toBe(1); ```
Verdict: FAIL - Issue: implementation detail. - Fix: assert UI output changes.
34
Gotcha: mocked fetch missing ok/status. ``` fetch.mockResolvedValue({ json: async () => ({ name: 'A' }) }); ```
Verdict: FAIL - Issue: mock shape incomplete. - Fix: include ok/status/json to match code.
35
Gotcha: fixed sleep. ``` await new Promise(r => setTimeout(r, 1000)); ```
Verdict: FAIL - Issue: brittle timing. - Fix: findBy/waitFor; fake timers for debounce.
36
Gotcha: fake timers not restored. ``` vi.useFakeTimers(); // no vi.useRealTimers() ```
Verdict: FAIL - Issue: leaks timer mode. - Fix: restore real timers in afterEach.
37
Gotcha: debounce test without advancing timers. ``` vi.useFakeTimers(); await user.type(input, 'a'); expect(fetch).toHaveBeenCalled(); ```
Verdict: FAIL - Issue: debounce not flushed. - Fix: advance timers before asserting.
38
Gotcha: userEvent + fake timers not configured. ``` vi.useFakeTimers(); const user = userEvent.setup(); ```
Verdict: PARTIAL FAIL - Fix: userEvent.setup({ advanceTimers: vi.advanceTimersByTime }).
39
Gotcha: findBy not awaited. ``` screen.findByText('Done'); expect(...); ```
Verdict: FAIL - Issue: findBy returns Promise. - Fix: await screen.findByText('Done').
40
Gotcha: async removal asserted sync. ``` expect(screen.queryByText('Loading')).not.toBeInTheDocument(); ```
Verdict: PARTIAL - Issue: removal may be async. - Fix: waitForElementToBeRemoved(() => screen.getByText(/loading/i)).
41
Gotcha: only asserting fetch call. ``` expect(fetch).toHaveBeenCalledWith('/api/users/1'); ```
Verdict: PARTIAL - Issue: asserts internals only. - Fix: also assert UI outcome (loading/success/error).
42
Gotcha: textContent exact match. ``` expect(node.textContent).toBe('1'); ```
Verdict: PARTIAL - Issue: brittle whitespace. - Fix: toHaveTextContent('1').
43
Gotcha: missing providers. ``` render(); // uses Router/QueryClient ```
Verdict: FAIL - Issue: missing provider wrapper. - Fix: render with Router/QueryClientProvider wrapper.
44
Gotcha: ambiguous role query. ``` screen.getByRole('button'); ```
Verdict: PARTIAL - Issue: ambiguous. - Fix: include name: { name: /save/i }.
45
Gotcha: no assertions at all.
Verdict: FAIL - Issue: test provides no signal. - Fix: add expects for behavior/outcome.
46
Gotcha: hardcoding i18n text.
Verdict: PARTIAL FAIL - Issue: brittle under localization. - Fix: role/name queries; stable labels or testids when needed.
47
Gotcha: asserting incidental order.
Verdict: PARTIAL - Issue: over-specified. - Fix: assert ordering rule only if required.
48
Gotcha: missing retry coverage.
Verdict: PARTIAL FAIL - Issue: error -> retry not tested. - Fix: simulate failure then success; assert error cleared.
49
Gotcha: direct value set on input. ``` input.value = 'x'; expect(input.value).toBe('x'); ```
Verdict: FAIL - Issue: bypasses React path. - Fix: user.type then assert visible behavior.
50
Gotcha: querySelector used. ``` const input = container.querySelector('input'); ```
Verdict: FAIL - Issue: implementation detail query. - Fix: getByLabelText/getByRole('textbox', { name: ... }).
51
Gotcha: testing classnames.
Verdict: FAIL - Issue: style details change often. - Fix: test behavior/roles/text instead.
52
Gotcha: waitFor used when not needed.
Verdict: PARTIAL - Issue: unnecessary async. - Fix: use getBy* for sync elements.
53
Gotcha: expecting exact error string.
Verdict: PARTIAL FAIL - Issue: brittle. - Fix: assert role=alert and key phrase.
54
Gotcha: missing button type causes accidental submit. ``` ```
Verdict: PARTIAL FAIL - Issue: default type is submit in forms. - Fix: set type='button' and test submit behavior.
55
Gotcha: mixing fake timers and promises poorly.
Verdict: PARTIAL FAIL - Issue: can stall updates. - Fix: advance timers and flush microtasks; prefer simpler patterns.
56
Gotcha: not verifying loading state.
Verdict: PARTIAL - Issue: misses key user-visible behavior. - Fix: assert loading appears then disappears.
57
Gotcha: asserting internal hook calls.
Verdict: FAIL - Issue: implementation detail. - Fix: assert UI outcomes.
58
Gotcha: over-mocking everything.
Verdict: PARTIAL - Issue: tests become meaningless. - Fix: mock boundaries (network) but keep behavior real.
59
Gotcha: multiple async steps without awaits.
Verdict: FAIL - Issue: ordering undefined. - Fix: await each userEvent/findBy step.
60
Gotcha: not using toBeDisabled matcher.
Verdict: PARTIAL - Issue: less readable assertions. - Fix: prefer expect(button).toBeDisabled().
61
Scenario: verdict + 3 issues. ``` test('search', () => { render(); fireEvent.change(screen.getByTestId('q'), { target: { value: 'a' } }); expect(fetch).toHaveBeenCalled(); }); ```
Verdict: FAIL 1) fireEvent for typing (prefer userEvent) 2) missing async handling/await 3) asserts fetch only; missing UI outcome assertions
62
Scenario: how to assert element appears after async fetch?
Use await screen.findByText/findByRole. Do not use fixed sleeps.
63
Scenario: negative assertion (sync).
Use queryBy* and expect(...).not.toBeInTheDocument(). Do not use getBy* for absence.
64
Scenario: best query for Save button.
getByRole('button', { name: /save/i }) (user-centric + a11y).
65
Scenario: spinner disappears.
Use waitForElementToBeRemoved(() => screen.getByText(/loading/i)) or assert final state with findBy.
66
Scenario: when to use fake timers?
For debounce/timeout/interval logic. Advance timers; restore real timers.
67
Scenario: preventing accidental submit.
Set secondary buttons to type='button'; test that clicking them does not submit.
68
Scenario: testing error UI accessibly.
Render error with role='alert'; assert using getByRole('alert').
69
Scenario: most common cause of flakiness.
Not awaiting async user interactions or DOM updates (missing await userEvent/findBy/waitFor).
70
Scenario: snapshot vs targeted assertions.
Prefer targeted assertions for behavior; snapshots only for small stable markup.
71
Scenario: wrapper needed for providers.
Wrap render with Router/QueryClient/Context providers if component depends on them.
72
Scenario: mock fetch vs MSW.
Mock fetch stubs a function; MSW intercepts network requests, closer to real behavior.
73
Scenario: why include name in getByRole?
Disambiguates elements and encodes intent; improves stability and a11y alignment.
74
Scenario: a good count assertion.
Prefer toHaveTextContent('1') on a role/name query (or testid if no better query exists).
75
Scenario: evaluator one-liner for brittle test.
FAIL - The test relies on implementation details and timing; rewrite using role/name queries, await async updates, and assert user-visible outcomes.
76
Checklist: RTL review top 5.
1) Query by role/name 2) Use userEvent + await 3) Handle async with findBy/waitFor 4) Avoid implementation details 5) Reset mocks/providers between tests
77
Checklist: async test toolkit.
await user.click/type findBy* waitFor waitForElementToBeRemoved mock network (MSW/fetch mock)
78
Checklist: mocking hygiene.
Reset/restore mocks in afterEach Avoid shared state Use consistent mock shapes Prefer MSW for integration-like tests
79
Checklist: fake timer steps.
useFakeTimers trigger action advance timers assert useRealTimers
80
Checklist: evaluator wording for tests.
Name issue (flaky/brittle/implementation detail), impact (unstable CI/false confidence), fix (role/name queries, await async, assert outcomes).