Stop Writing Brittle Tests: The Smart Locator Strategy Every Playwright Dev Needs

Published: (February 18, 2026 at 05:18 PM EST)
2 min read
Source: Dev.to

Source: Dev.to

I spent hours debugging a flaky test that broke because of one line of XPath.
The fix? A single Playwright locator that took 10 seconds to write.

Here’s what I wish I knew earlier.

The Hard Truth

If your tests are flaky, your locators are the problem.

Traditional XPath and CSS selectors navigate the DOM like a tree. The moment a developer reorders elements or changes structure — your tests break. Every. Single. Time.

// Brittle – breaks if DOM structure changes
page.locator('div#main > ul:nth-child(2) > li:first-child > button')

Human Vision vs Robot Vision

Playwright takes a completely different approach. Instead of hunting for IDs and class names (robot vision), it targets elements the way a real user would — by what they see on screen (human vision).

The gold standard? page.getByRole()

It taps into the accessibility tree — the same structure screen readers use. If a screen reader can find it, Playwright can test it. And it survives refactors.

// Smart – survives DOM changes
page.getByRole('button', { name: 'Sign up' })

The Locator Priority Hierarchy

Follow this order and you’ll solve 80 % of your locator problems:

  1. getByRole – Always start here. Works for buttons, headings, checkboxes, links, and more.
  2. getByText / getByLabel – Fall back to visible on‑screen text or form labels.
  3. getByTestId – Ask your dev to add a data-testid attribute. It’s stable and won’t change by accident.
  4. CSS/XPath – Last resort only. Even then, paste it into an LLM – it’ll convert it to a getByRole locator for you.

Bonus: No More sleep() Hacks

Playwright’s built‑in auto‑wait means every locator automatically waits for the element to be attached, visible, stable, and enabled before acting. No more arbitrary timeouts.

// You no longer need this
await page.waitForTimeout(3000);
// Preferred approach using the accessibility tree
const submitButton = await page.getByRole('button', { name: 'Submit' });
await submitButton.click();

This is Part 2 of my Playwright 80/20 Pareto Principle series. Part 3 covers Playwright Actions — coming soon!

🎬 Watch the full video here: https://www.youtube.com/watch?v=6IaCQ70cNVc

0 views
Back to Blog

Related posts

Read more »

OpenClaw Is Unsafe By Design

OpenClaw Is Unsafe By Design The Cline Supply‑Chain Attack Feb 17 A popular VS Code extension, Cline, was compromised. The attack chain illustrates several AI‑...