All posts
Guides3 min read

Why your end-to-end tests keep breaking

Most end-to-end suites don't fail because the product broke. They fail because a selector moved. Here's what actually causes the churn.

A selector-based test failing on a DOM change while a screen-reading agent passes

Your CI went red overnight. You open the failure, and the checkout button is right there on the screenshot, exactly where it has always been. Nothing is broken. Someone wrapped the form in a div.

This is the normal condition of end-to-end testing, and it is why most suites quietly stop being trusted. Not because they catch nothing, but because they cry wolf until nobody reads them.

Selectors are a promise the UI never made

A selector-based test does not look for the button. It looks for a path to the button: a class, a test id, a position in the tree. That path is an implementation detail. Nobody on the team agreed to keep it stable, and no review ever asks "does this rename break a selector?"

A button moves in the DOM. The selector-based test fails even though nothing is broken; the screen-reading agent still finds the button and passes.

So the path drifts. Every redesign, every component extraction, every CSS refactor moves something. The test fails, an engineer spends twenty minutes confirming the product is fine, and updates the selector. Multiply by a hundred tests.

The cost is not the twenty minutes. It is that after a few rounds of this, a red build stops meaning anything.

What changes, versus what matters

A test should fail whenA test should not fail when
The pay button never enablesThe pay button moved to a new parent
Checkout charges the wrong totalThe class went from .primary to .btn
Sign-up silently drops the passwordA designer changed the padding
The confirmation screen never loadsThe DOM gained a wrapper div

Everything in the right column is churn. A suite that cannot tell the two columns apart is a suite that will eventually be ignored.

Describe the intent, not the path

The alternative is to anchor the test to what a person would do, because that is the thing you actually promised your users:

Sign up, add an item to the cart, and check out.

That sentence is the whole test. A vision agent reads the rendered screen and decides what to tap, the same way a person scanning the page would. When the button moves, it is still the button that says "Place order", so the test still passes. When the button never enables, the test fails, which is the point.

The same sentence drives a real Android or iOS device and a real browser, so one description covers your app and your dashboard. A web check needs no build artifact at all, just a URL, which means it can run on a pull request before anything is compiled.

Start with three

If you are rebuilding a suite that nobody trusts, do not port all hundred tests. Three flows are worth more than thirty:

  • Sign-up, all the way to a usable account
  • Checkout, all the way to a confirmed order
  • Whatever your support inbox complained about most last month

Every run gives you a video, a screenshot of each step, and a stated reason for what the agent did, so when one does go red a reviewer can watch the failure instead of guessing at it.

Start free and have the first one running in about five minutes.