How self-healing test automation actually works
"Self-healing" gets attached to almost every test tool now. Most of them mean one of two things: try a second selector, or ask a model to guess a third. Here's what actually happens when a locator breaks — the full ordered chain, and the parts of it that are designed to refuse rather than guess.
What breaks, and what it costs
A locator is a bet on how you'll find the same element again tomorrow. Record
a click on a button today, and the test stores something like an ID, a class, or
the button's visible text. Ship a release where a developer renames that ID,
swaps the button for a styled <div>, or the build pipeline
regenerates a class name — and the bet is void. The test doesn't know the button
moved; it knows the string it was looking for no longer exists.
The traditional answer is a person: someone opens the failing test, finds the new selector by hand, and updates it — for every test that touched that element, every time the UI changes. That maintenance loop, not writing the first test, is the real ongoing cost of browser automation. It's also exactly the work "self-healing" claims to remove.
What the phrase usually hides
Two common implementations sit under that one label. The first tries a stored backup selector when the primary fails — useful, but only if a working backup was ever captured. The second sends the current page to a language model and asks it to point at whatever looks closest to the original — which works often enough to feel magic, and fails in a specific, dangerous way: silently. A model that's 80% confident in the wrong element still returns an answer, the click still lands somewhere, and the step still reports green. A false pass is worse than a red one, because nobody goes looking for a test that says everything's fine.
The cascade, in order
QAright records more than one locator per element to begin with — up to fourteen ranked candidates, not a single string with nothing behind it. When a step's primary locator fails, it works down a fixed chain. Each phase is logged with what it tried and what it decided, so a repair is something you can read back and disagree with, not a black box that quietly did something.
Primary locator
The highest-scoring candidate from record time — and the score isn't just "ID beats class beats text." An attribute stability check runs first: an ID like
login-btnand one likea7x9f2are both "an ID," but only one of them will still be there next week. The generated one is scored down before it's ever trusted as primary.Fallback candidates
The remaining ranked candidates, tried in order — each one re-checked for both presence and uniqueness against the live page. A candidate that now matches three elements instead of one isn't "found," it's ambiguous, and gets skipped rather than clicked on a guess.
App model lookup
The element is matched against a model built by crawling your app, not just the single page it happened to be recorded on — useful when the same control exists elsewhere with the DOM structured slightly differently.
Semantic scan
Every interactive element currently on the page is scored against the DOM snapshot captured at record time — tag, text, ARIA role, label, on-screen position, element type. This phase is held to a deliberately higher bar than a human-reviewed suggestion would need, for one reason: nobody is reviewing it in the moment. A wrong match here produces a passing step against the wrong element, which is a worse outcome than a failing one.
AI-assisted repair
Optional, off by default, and last — not first. If you enable it, the snapshot goes to a model you configure, only after every deterministic phase above has already failed.
What it refuses to do
Most flaky automation doesn't come from a tool being unable to find an element. It comes from a tool guessing confidently when it shouldn't. The cascade above is deliberately conservative in four specific ways:
- Never heal an assertion.Finding the element and evaluating what it says are different questions. A broken path to an element is recoverable; an element found in the wrong state is a real failure and gets reported as one, not papered over.
- Never resolve ambiguity by guessing.If a candidate matches more than one element and scores poorly, it's refused rather than picked at random.
- Never invent a candidate.Healing re-ranks what capture already recorded. It doesn't fabricate a new selector from a stale DOM mid-run.
- Never silently rewrite your test.A fallback only gets promoted to primary after it's won three runs in a row — and the promotion itself is written to an audit log, not applied invisibly.
When the cascade itself can't tell
Sometimes none of the five phases resolve cleanly, and the honest answer is "a person should look at this." That's a different job from in-run healing, which has milliseconds to decide and has to be conservative enough to run unattended. A repair agent that runs after the fact can take longer and do more: replay the test up to the failing step — so the app is actually in the state that step expects, not wherever the recorded URL happens to load, which for anything behind a login is nowhere useful — diagnose using the failure the run already classified, propose a candidate from the live DOM, and then prove it by executing both the failing step and the one after it. "Found an element" and "found the right element" look identical right up until something downstream depends on the difference.
The actual feature is what you can check
None of the above is worth much if it can't be inspected. Every healing event — step, attempt number, strategy used, outcome — is logged and feeds a report you can read, not a pass/fail summary that hides how the pass happened. If a fallback is doing the work every run, that's visible, and it's promoted openly instead of quietly carrying the test forever.
Keep reading. Healing starts with the locators recorded in the first place; the order worth choosing them in is choosing locators that survive UI changes. A test that fails for a reason healing cannot fix is often a timing problem, covered in flaky tests, and test drift detection reports a changed page before any test has to heal around it.
See the cascade run against your own app, on the free plan — no card, no time limit.