Skip to content

Writing Stable Selectors with the Browser Library

05-08-2026 Roy de Kleijn
Writing Stable Selectors with the Browser Library

Most flaky web tests are not the framework's fault. They fail because the selector, the way you point the Browser library at an element, breaks the moment the page changes. Stable selectors are the single biggest thing you can do to keep a suite green, so they are worth getting right early.

Why selectors break

Three patterns cause most of the trouble:

  • Position. Selectors that rely on an element being the third item in a list break as soon as the order changes.
  • Generated classes. Class names produced by build tools change between releases, so a selector tied to one is living on borrowed time.
  • Deep paths. Long chains that mirror the page layout snap the moment someone restructures the markup.

All three tie the test to how the page is built rather than to what the element is. That is the root of the problem.

The preferred way: a custom test attribute

Whenever you can add attributes to the application, select by one added for testing, such as data-testid, data-test, or any custom attribute your team agrees on:

Click    css=[data-testid="login"]

This is the most stable option there is. The attribute exists only to identify the element, so it does not move when the design changes, and, importantly, it does not change when the interface language changes. It is an explicit contract between the application and the tests, which is exactly what you want. Make this your default whenever it is possible.

When you cannot add attributes

Sometimes you cannot touch the markup: a low-code or no-code platform, a legacy application, or simply no access to the front-end code. Then fall back, in rough order of preference.

Accessible role and name. Find the element the way a user or a screen reader would, by its role, such as button or link, and its accessible name:

Click    role=button[name="Log in"]

It depends on the element's purpose rather than its markup, and it has a useful side effect: if it cannot find the element, that often means a missing role or label, which is a real accessibility problem your tests have just surfaced.

Visible text. Simple and readable, when the text is unique enough:

Click    text=Log in

A real id. A stable id is a good anchor:

Click    id=login

Avoid position and generated classes. If you find yourself counting elements or copying a long class string from the browser's dev tools, treat it as a warning sign rather than a solution.

A note on multi-language sites

If your application ships in more than one language, be careful with anything that depends on visible text or an accessible name, because those are translated. text=Log in works in English and fails in Dutch. This is another reason a custom test attribute is the preferred choice: it is the same in every language. Where you must select by text on a multi-language site, drive it from the same localised strings the application uses, rather than hard-coding one language.

Let strict mode help you

The Browser library runs in strict mode, which means a selector that matches more than one element fails rather than silently acting on the first match. That is a feature, not an obstacle. When it complains, it is telling you the selector is ambiguous, which is exactly the kind of fragility you want to catch while you are writing the test, not in a failing pipeline later.

Keep selectors in one place

Even a good selector should not be scattered across dozens of tests. Keep them in variables or inside page-level keywords, so when the application changes you update one line rather than hunting through the whole suite. This is where selectors meet user keywords and resource files, and it is what keeps a suite maintainable as it grows.

*** Variables ***
${LOGIN_BUTTON}    css=[data-testid="login"]

*** Keywords ***
Submit Login
    Click    ${LOGIN_BUTTON}

Change the selector once, and every test that logs in follows.

Capturing selectors quickly

If you are hunting for a selector by hand, a recorder can speed it up. robotframework-browser-recorder, a tool we maintain, records your clicks in the browser and writes out the Browser library keywords, selectors included. It is on PyPI and installs with uv:

uv add robotframework-browser-recorder

One caveat matters here. A recorder captures whatever selector it can find, which is often a brittle one. Treat its output as a draft, and upgrade those selectors to the stable choices above, a custom test attribute first, before you rely on them.

Go further

Reliable selectors are a habit more than a trick, and the judgement of when to use text, roles or test ids comes with practice. Our Robot Framework Certified Professional course covers selectors and reliable web automation as part of the full picture, and prepares you for the RFCP exam.

Frequently asked questions

What is the most reliable selector? An attribute added for testing, usually data-testid, because it exists only to identify the element and does not change when the design does.

Why do my tests break after a redesign? Usually because the selectors were tied to layout, position or generated class names. A custom test attribute, or failing that a role, text or id selector, avoids that.

What is strict mode? The Browser library fails a selector that matches more than one element, instead of guessing. It catches ambiguous selectors early, which makes your tests more reliable.

Should I select by accessibility role? It is a strong fallback when you cannot add a custom test attribute. Selecting by role and accessible name mirrors how users and assistive technology find an element and surfaces missing roles or labels. Note that the accessible name is translated, so on multi-language sites a custom test attribute is more reliable.

What about multi-language sites? Selectors that depend on visible text or an accessible name break when the language changes, because those strings are translated. Prefer a custom test attribute such as data-testid, which is the same in every language.


Next: back to web testing with the Browser library, or see user keywords and resource files.

Share:
Roy de Kleijn

Test Automation Expert

With over 15 years of experience in software testing and test automation, Roy helps teams improve their testing processes.

Get Robot Framework certified

Go from the basics to exam-ready at your own pace with the online RFCP course.