Skip to main content
AI-Powered Testing·Lesson 3 of 5

Self-Healing Test Suites

The most common reason test suites fail is not because bugs were introduced — it's because the UI changed and the test selectors became outdated. A developer renames a CSS class, restructures the DOM, or changes button text, and suddenly 20 tests are failing for the wrong reason.

Self-healing tests address this by automatically adapting to UI changes.

How Self-Healing Works

Self-healing tools (like Healenium, Applitools, and Reflect) work by storing multiple ways to find the same element. If the primary selector fails, the tool tries fallback selectors — using the element's position, nearby text, ARIA role, or other attributes to locate it.

When a fallback succeeds, the tool logs the selector change and suggests updating the primary selector. The test passes and a human reviews the suggested update later.

Implementing Selector Resilience Without Third-Party Tools

You can achieve most of the benefits of self-healing by writing resilient selectors in the first place.

Prefer role and label selectors:

// Fragile — breaks if the class changes
page.locator('.submit-btn-primary')

// Resilient — tied to the semantic role and visible label
page.getByRole('button', { name: 'Submit Order' })

Use test IDs for complex elements: When there's no good role or label to target, add a data-testid attribute to the element:

<div data-testid="checkout-summary">...</div>
page.getByTestId('checkout-summary')

Test IDs are stable by design — developers know not to change them without updating tests.

Use text selectors for content that is stable:

page.getByText('Your order has been confirmed')

This is resilient to DOM restructuring as long as the text itself doesn't change.

Building a Selector Strategy

Adopt a selector priority order for your team:

  1. getByRole with name — most resilient, most semantic
  2. getByLabel — for form fields
  3. getByText — for stable text content
  4. getByTestId — when no semantic selector is available
  5. CSS selector — last resort, most fragile

Document this in your team's testing guidelines so everyone follows the same approach.

Handling Legitimate UI Changes

When the UI changes intentionally (a button is renamed, a section is restructured), your tests should update to match. The goal of resilient selectors is to reduce false failures — not to hide legitimate changes.

When a test fails after a UI change:

  1. Verify it's a selector issue, not an actual bug
  2. Update the selector to match the new UI
  3. Commit the selector update with the UI change in the same PR

This keeps tests and UI in sync and makes the history readable.