intermediate
Avoiding brittle selectors
Prefer user-facing roles, labels, and explicit test contracts over implementation selectors that change during harmless refactors.
Brittle selectors break on harmless refactors: CSS modules hashes, nth-child, deep DOM paths, and IDs generated at runtime. Prefer accessible names—role, label, placeholder—and treat `data-testid` as an explicit contract between dev and QA when semantics are insufficient.
In Playwright, `getByRole('button', { name: 'Save' })` survives markup changes that preserve UX.
On interviews: rewrite a brittle XPath/CSS selector into Testing Library queries and explain the maintenance difference.
Common pitfalls: test IDs on every div, selecting by visible text that changes with i18n, and coupling to third-party widget internals.
The trade-off is slightly more upfront accessibility work versus dramatically lower E2E churn.
Checklist:
- Query priority: role, label, text, then test id.
- Add test ids only for non-semantic targets.
- Avoid selectors tied to layout structure.