Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Use data-testid when a test needs a stable, explicit way to find an element that a role, label, or meaningful text cannot reliably identify. For interactive controls, start with user-facing locators such as Playwright’s getByRole() or a label query when those reflect the behavior you want to verify. Test IDs are resilient to styling and copy changes, but they do not prove that the interface remains accessible.

What is data-testid?

data-testid is a custom HTML data attribute that gives a test a deliberate identifier for an element, for example <button data-testid="checkout-submit">Place order</button>. Playwright’s getByTestId() uses data-testid by default, and Testing Library offers a corresponding getByTestId() query.

Unlike a CSS class or generated DOM ID, a test ID is intended to be a selector contract for automated tests. It can stay unchanged while styles, layout, or visible wording evolve, provided the team keeps the attribute stable.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Should you use data-testid or a semantic locator?

Choose a locator based on what the test is meant to protect. A role-and-name query can verify that a control is exposed to users and assistive technology in the expected way; a test ID verifies that a specific element carries the agreed test identifier. Neither is universally better, but they catch different regressions.

Selector Accessibility semantics Styling and copy changes Localization Repeated components What a passing lookup establishes
Role and accessible name Tests a user-facing role and name. Independent of styling; may fail if the accessible name changes. May need locale-aware names or test setup. Can be ambiguous if several items share a role and name; scope the query to a specific component when needed. The intended role/name locator matched; it does not by itself prove all accessibility requirements.
Label Tests the label exposed for a form control. Independent of styling; may fail if the label changes. Label text may vary by locale. May require scoping when forms contain repeated labels. A control matching the label was found.
Visible text Checks visible wording, not necessarily the element’s semantic role. Can fail on a copy change. Often requires locale-specific expectations. Repeated text can match more than one element. The expected text was found; it does not establish that the element is accessible or interactive as intended.
data-testid Does not express accessibility semantics. Can remain stable through styling and copy refactors if the identifier is kept stable. Can avoid coupling the selector to localized copy. Use a meaningful identifier and scope it to distinguish repeated instances; avoid position-based IDs. An element with that explicit test contract was found, not that a user can perceive or operate it.
CSS class, XPath, generated ID, or index Usually does not express a user-facing contract. Often coupled to styling, DOM structure, generated values, or element order. Usually not tied to copy, but that does not make it a sound contract. Position and structure changes can select the wrong element or break the query. The current implementation matched the selector, which may not reflect intended behavior.

Testing Library puts role and other semantic queries ahead of getByTestId(), recommending the latter when no suitable user-facing query exists or when content is dynamic. Playwright describes test IDs as especially resilient when text or roles change, while also recommending user-facing attributes and explicit contracts. ( Testing Library query priorities; Playwright locators.)

When is a test ID the right choice?

  • Dynamic content: Use an explicit identifier when the text changes unpredictably but the target component remains the same.
  • Copy or localization may change: If the test is not intended to validate the exact wording, a test ID avoids tying element selection to that wording.
  • Repeated or non-semantic structures: A test ID can distinguish a target when the markup has no useful role or label, or when several similar components need a stable identifier.
  • Intentional decoupling: Use one when the team wants a stable test contract independent of styling and incidental DOM structure.

Prefer a role, label, or text locator when it captures a requirement users should experience. For example, if the test must confirm that the checkout action is announced as a button named “Place order,” selecting by role and name keeps that user-facing contract in the test.

How do you add and use data-testid?

  1. Add a meaningful attribute: Put a stable identifier on the element, such as data-testid="checkout-submit".
  2. Choose the locator that matches the requirement: In Playwright, use page.getByRole('button', { name: 'Place order' }) when the accessible role and name are the behavior under test. Use page.getByTestId('checkout-submit') when wording is dynamic or localized and the test needs the explicit contract instead.
  3. Use the equivalent query in your test library: Testing Library supports getByTestId(); reserve it for cases where a suitable user-facing query is unavailable or inappropriate.
  4. Keep the identifier independent of implementation details: Do not encode a CSS class, generated value, or DOM position in the name.
<button data-testid="checkout-submit">Place order</button>
// Prefer the user-facing contract when it is stable
await page.getByRole('button', { name: 'Place order' }).click();

// Use the explicit test contract when copy is dynamic or localized
await page.getByTestId('checkout-submit').click();

How do teams keep test IDs useful?

  • Set a team-wide convention: Agree on the attribute and a readable naming pattern, such as component-action.
  • Add them selectively: Avoid placing IDs everywhere when roles, labels, or relevant text already provide the right contract.
  • Keep names stable and meaningful: An identifier should describe the target’s purpose, not its current styling or location.
  • Scope repeated elements: If several components share an identifier, use the test framework’s scoping approach to select the intended instance rather than relying on order.
  • Configure the attribute if needed: Playwright and Testing Library can be configured to use an attribute other than data-testid when a team standard calls for one.
  • Test accessibility separately: A test ID can keep finding an element after its role, name, or other semantics regress. Add accessibility assertions and appropriate automated or manual accessibility checks where those properties matter.

Cypress recommends data-* attributes to separate selectors from CSS or JavaScript changes, but selector resilience is not a substitute for accessibility testing. ( Cypress best practices.)

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

What data-testid can—and cannot—fix

A test ID can reduce breakage caused by incidental implementation changes when the ID remains stable. It cannot make a test meaningful if the test checks only that an element exists, nor can it establish that a user can find, understand, or operate that element. A suite built entirely around test IDs may keep passing even after visible text, accessible names, or roles change in ways users would notice.

There is no defensible published percentage for how much test IDs alone reduce flakiness or maintenance. The practical gain depends on whether selector breakage is actually caused by styling, copy, or markup churn, and on how consistently the team maintains its test contracts.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.