React - Testing (Advanced)

Overview

Estimated time: 20–30 minutes

Extend the simple in-browser test harness to cover async behavior and user-driven interactions.

Try it: Async component tests

View source
function assert(name, condition){
  const el = document.getElementById('results-adv');
  const li = document.createElement('li'); li.textContent = `${condition ? 'PASS' : 'FAIL'} - ${name}`; li.style.color = condition ? 'limegreen' : 'salmon'; el.appendChild(li);
}
function sleep(ms){ return new Promise(r => setTimeout(r, ms)); }
function Counter(){
  const [n, setN] = React.useState(0);
  async function incAsync(){ await sleep(50); setN(x => x+1); }
  return (<div><span id="val">{n}</span> <button onClick={incAsync}>+</button></div>);
}
async function runTests(){
  const root = document.getElementById('sandbox-adv');
  const r = ReactDOM.createRoot(root); r.render(<Counter />);
  assert('initial is 0', root.querySelector('#val').textContent === '0');
  root.querySelector('button').click();
  await sleep(60);
  assert('increments after async', root.querySelector('#val').textContent === '1');
}

    Syntax primer

    • Wrap tests in async functions and await UI updates driven by timers or promises.
    • Arrange → Act → Assert: render the component (arrange), fire an interaction (act), then check expectations (assert).
    • Use small helpers like sleep(ms) when simulating async work (timeouts, network).

    Vocabulary

    • Assertion: a check that must be true for the test to pass.
    • Async test: a test that awaits an operation that completes later (e.g., a Promise).
    • Act: the step where you simulate user input or time passing.

    Common pitfalls

    • Racing assertions before the UI updates; always await the condition change.
    • Triggering React state updates outside React without giving React a chance to flush (await microtasks or small timeouts as needed).

    Exercises

    1. Extend the Counter to support a "−" button with an async decrement and add a passing test.
    2. Add a loading indicator ("Saving…") shown while the async increment runs; assert it appears and then disappears.
    3. Write a retry button that increments only after two clicks (simulate by counting clicks) and test the behavior.

    Checks for Understanding

    1. What pattern helps structure tests for readability?
    2. Why do async UI tests require awaiting updates?
    Show answers
    1. Arrange → Act → Assert clarifies setup, interaction, and expectations.
    2. Because state updates and DOM changes happen later; without awaiting, assertions can run too early.