What is the data-testid attribute?
Software Engineering Team Lead and Director of Cloudsure
TL;DR
data-testid
attribute is used in web applications to identify elements specifically for automated testing, without affecting the user experience or design.- Manual testing challenges involve clicking through the interface and checking functionality across different environments, which can be fragile with changing designs.
- Automated testing helps but can be fragile when relying on class names or IDs that change due to design updates.
data-testid
provides resiliency by giving DOM elements a unique identity for tests, ensuring they remain stable even as the UI changes.- Consistency in usage is critical for maintaining a reliable testing strategy; developers must incorporate
data-testid
from the start. - Regular test maintenance is important to ensure tests stay relevant as the application evolves, requiring periodic updates to
data-testid
attributes. - Test visibility and quality are essential; tests should provide meaningful feedback, and CI/CD pipelines should include regular test runs to ensure high code quality.
I recently came across an unfamiliar attribute – data-testid
– in a web application I was working on. It turned out that this trusty little fellow is used as a naming convention in automated testing.
Challenge
Once upon a time (like a few minutes-or-so ago), testing a website meant systematically clicking through the interface from one page to the next. “Does this button still work? What happens if I click here? What happens on different environments? How do I check things with A/B testing? What if a feature can be toggled or presented in multiple states?” You get the picture 😄 It’s a little like finding Waldo, except there were more buttons, bugs, and fewer striped shirts.
Automated testing is trying to become the new normal, but there’s a catch. The very thing that makes websites beautiful and user-friendly – those sleek designs – also makes them fragile for testing, especially as designs change over time.
DOM elements often hide deep within nested div
s or are dynamically generated. While you could use class names or IDs, this approach often leads to broken tests due to changes in styling or structure.
An option
The data-testid
attribute gives elements a secret identity, making it easier to uniquely target them in tests.
Resilience
Unlike class names or IDs, data-testid
is more reliable because it’s meant specifically for testing and can be agnostic to design updates. It can be used semantically and it doesn’t affect the user experience.
Since data-testid
is used specifically for testing, it won’t change just because someone decides to make buttons round instead of square. It’s there for you through thick and thin, helping you target exactly what you need without getting swept up in design trends.
Consistency
While data-testid
can be a lifesaver for automated testing, it requires discipline to ensure it’s consistently used. In fast-paced environments, developers might use ad-hoc class names or IDs. To avoid this, developers need to make a habit of incorporating semantic data-testid
values from the start taking a stance at user behavior instead of design.
Maintaining consistency across the team is crucial. A single developer neglecting to use data-testid
can weaken the testing strategy. Team leads and code reviewers play a critical role in enforcing this practice by making it part of coding standards.
Maintenance
As your application evolves, it’s easy for tests to fall out of sync with the codebase. Developers need to continually revisit and refactor tests to ensure they reflect the current state of the application. This includes updating data-testid
attributes and ensuring tests aren’t overly reliant on UI details that might shift.
Visibility
Visibility into test quality is key. Automated tests should not only pass but also provide meaningful feedback. Well-written tests serve as a safety net for developers making changes, allowing them to refactor confidently without fear of introducing bugs.
By running tests regularly as part of the CI/CD pipeline and ensuring all team members are aware of test failures, you foster a culture where quality is consistently prioritized.
Implementation
Unit tests
- Jest: jestjs.io
- Mocha: mochajs.org
- Jasmine: jasmine.github.io
- Enzyme: enzymejs.github.io/enzyme
End-to-end tests
- Cypress: www.cypress.io
- Puppeteer: pptr.dev
- Selenium: www.selenium.dev
- Playwright: playwright.dev
Examples
These examples focus on behavior rather than design tests. They highlight functionality rather than specific UI elements using class names or IDs.
Button click
In this case, you want to test the behavior triggered by a button click rather than its appearance or specific class.
<button
id="btnSubmit"
class="btn-round"
data-testid="buy-now-button"
type="button"
onclick="submitForm()"
>
Buy now
</button>
In the test, you'd focus on checking that the submitForm
function was called when the button was clicked, regardless of where the button is placed on the page, how the button looks, what it is called or what is used to define how it is styled.
Change the input field value
Here, you test the behavior of an input field updating its value.
<input
data-testid="username-input"
type="text"
placeholder="Your username"
oninput="handleInput()"
/>
The test would verify that handleInput
is invoked as the user types, focusing on behavior rather than the field’s specific design.
Toggling visibility of an element
In this example, you test the behavior of an element appearing or disappearing.
<div data-testid="dropdown-content" style="display:none;">
<p>Dropdown content here.</p>
</div>
<button data-testid="toggle-button" type="button" onclick="toggleDropdown()">
Toggle Dropdown
</button>
Your test would check that the toggleDropdown()
function shows or hides the dropdown-content
div
, ensuring functionality works regardless of any style changes.
Form submission
Testing form behavior without relying on how it looks visually or is structured with class names.
<form data-testid="login-form" onsubmit="handleLogin(event)">
<input
data-testid="email-input"
type="email"
placeholder="Enter email"
required
/>
<input
data-testid="password-input"
type="password"
placeholder="Enter password"
required
/>
<button data-testid="login-button" type="submit">Login</button>
</form>
In the test, you would verify that the handleLogin()
function is triggered on form submission, focusing on the submission process and validation rather than the layout of the form.
Conclusion
data-testid
is a powerful tool for enhancing automated testing in web applications. By providing a stable way to identify elements, it ensures that tests remain reliable even as the UI evolves. However, its effectiveness depends on consistent usage and regular maintenance to keep tests aligned with the application’s current state.