In a design-system suite, when should a Playwright component test update a mounted component instead of unmounting and mounting a fresh one?
answer
- Ask what the step is claiming
- Transitions versus starting points
- Speed is not the deciding axis
- Failures must name their own scenario
basics
~10 sUpdate when the prop transition itself is what you are asserting; mount fresh when the scenario is independent. Long update chains run faster but couple assertions together and make a failure hard to localise.
solid answer
~50 sDecide by what the step claims, not by what runs faster. `update` claims something about a transition — given this state, changing these props does that — and a design-system grid that must keep an open row menu when new rows arrive can only be asserted that way. A fresh `mount` claims something about a starting point, and that is what you want when the scenario is independent, when first-render behaviour is the subject, or when leftover state could make an assertion pass for the wrong reason. The cost of long update chains is paid at read time: a failure at step seven implicates all seven steps, and a design-system suite is read by people who did not write it. The workable rule is one mounted instance per behaviour, with as many updates as that behaviour genuinely spans, then `unmount()` or a new test. In Playwright 1.63 both routes stay available in the same test.
code
typescript · 11 linesimport { test, expect } from '@playwright/test';
test('toast keeps its dismiss focus when the message changes', async ({ mount }) => {
const toast = await mount('toast-default', { variant: 'info', message: 'Saved' });
await toast.getByRole('button', { name: 'Dismiss' }).focus();
await toast.update({ variant: 'info', message: 'Saved to drafts' });
await expect(toast.getByRole('button', { name: 'Dismiss' })).toBeFocused();
await toast.unmount();
});go deeper
Start with the simple habit: one scenario per test, mounted fresh. Reach for update only when the question you are asking is what happens to this component when its props change.
Justify the choice in terms of the claim being made. Transitions need update because a re-mount cannot observe them, and independent scenarios need a fresh mount so leftover state cannot decide the result.
Weigh diagnosability against runtime. Know that a failure inside a long chain implicates every earlier step, and that order-dependent failures are the observable symptom of scenarios that should have been separated.
Own the convention and its foundation. Phrase the rule as a claim test rather than a performance tip, enforce it in review, and make sure the harness contract behind unmount really discards the scenario, because every isolation guarantee rests on it.
## The decision, stated plainly Every component test is a sequence of renders, and for each step after the first you choose between re-rendering the instance you have (`update`) and starting a new one (`unmount` plus `mount`, or simply the next test). The choice is usually framed as a speed question. It is really a question about **what the test is claiming**. - `update` claims something about a **transition**: given a component in this state, changing these props does that. - A fresh mount claims something about a **starting point**: given these props from scratch, the component renders this. If the claim you want to make is a transition claim, no amount of re-mounting can express it. If it is a starting-point claim, an update chain is a slower and less honest way of reaching it. ## Where an update chain earns its place 1. **The transition is the product requirement.** A design-system data grid must keep an open row menu and its scroll offset when a new page of rows arrives. That requirement literally cannot be asserted after a re-mount. 2. **The bug class is prop-read-once.** Components that read a prop at first render and never again pass every re-mount test and fail in production. Update is the only way the suite sees them. 3. **The state is expensive to reach.** When getting the component into position took several interactions, updating props from there tests more of the real path than rebuilding the position from scratch. ## Where a fresh mount is the right call - The next scenario is independent, so carried-over state can only produce passes and failures for the wrong reason. - You are asserting first-render behaviour: an effect that must run once, an initial focus target, a default that only applies at mount. - The chain has grown long enough that a failure at step seven no longer tells anyone which scenario broke. - Unwinding state by hand would take more code than mounting again, and that unwinding is itself untested logic in the test. | Question about the step | Prefer `update` | Prefer a fresh mount | |---|---|---| | Is a transition the claim? | yes | no | | Must first-render effects re-run? | no | yes | | Is the scenario independent? | no | yes | | Is diagnosability the priority? | no | yes | ## The cost nobody prices in Speed is the argument usually made for long update chains, and it is real: a mount pays for a render and the teardown before it, while an update pays for a re-render. But the cost of a chain is paid by whoever reads the failure. A test with twelve updates and twenty assertions fails at one line and implicates all twelve steps; the reader must reconstruct the state at that point before they can judge whether the component or the test is wrong. Test suites are read far more often than they are written, and a design-system suite is read by people who did not write it and do not know the component. The reasonable middle: one mounted instance per behaviour, with as many updates as that behaviour genuinely spans, and a new test when the behaviour changes. That keeps transition coverage while making every failure name its own scenario. ## Setting the policy for a team - **Write the rule down as a claim test**, not as a performance rule: "use update when the prop change is what you are asserting." Rules phrased as "prefer update, it is faster" reliably produce chains. - **Make unmount explicit** at the end of a scenario that continues in the same test, so the intent to discard state is visible rather than inferred. - **Cap chain length in review.** The number matters less than having one; the discussion it triggers is the point. - **Watch for order-dependent failures.** A test that only fails after its neighbours is state leaking across scenarios, and it is a signal that a fresh mount was owed somewhere. - **Keep the harness contract stable.** `window.mount` and `window.unmount` are what every one of these decisions rests on; if unmount does not truly tear the scenario down, "fresh mount" stops meaning what the policy assumes and every isolation claim in the suite weakens at once.
- How would you know a suite has drifted into over-long update chains?Failures stop naming a scenario. Reports point at one assertion in a long test, reviewers ask what state the component was in, and tests start failing only in file order. Any of those is a signal that scenarios were merged into one mounted instance that should have been split.
- What breaks in this policy if unmount does not fully tear the scenario down?Every isolation claim in the suite weakens at once, because 'fresh mount' silently stops meaning fresh. Residual DOM or listeners bleed into the next scenario, so tests pass for the wrong reason. The harness contract is shared infrastructure, and its correctness is a precondition for the rule.
- Is speed ever a good enough reason on its own to prefer update?Rarely, and it is the wrong frame. If the run is too slow the fix is usually elsewhere — parallelism, a leaner harness bundle, fewer redundant scenarios. Trading diagnosability for seconds in a suite read by people who did not write it is a bad exchange to make deliberately.
saying these in an interview costs you the question
- Chooses update purely because mounting is slower
- Chains a dozen updates through one test
- Never asserts a transition, only first renders
- Assumes state cannot leak between scenarios
- Treats unmount as optional cleanup with no effect