Dom Elements Selection: data-testid
Explore how to select stable DOM elements for end-to-end tests using the data-testid attribute. Understand why common selectors can cause fragile tests and learn to implement dedicated attributes that improve test reliability and error diagnosis.
We'll cover the following...
data-testid
The following is a deterministic event test:
As we’ve seen, one of the defects of the above test is its uselessness while something goes wrong while retrieving the elements.
The HTML of the RealWorld form is as follows:
If we want to avoid using the order of elements for the foundation of our test, we can leverage:
-
The type: text, email, and password
-
The placeholder
The test could become the following (based on the type)
or, leveraging the placeholder
When everything is normal, both the type and placeholder work as expected. But if the email input field is not rendered correctly, they also help us to understand what is wrong.
What not to do
There are a ton of selectors we can use when finding DOM elements, but most of them could change because of different purposes (and then break the test inadvertently):
| Selector | Purpose |
|---|---|
| classes | Style |
| ids | JavaScript |
| HTML tags | standardization/SEO |
| type | User experience |
| ARIA attributes | User experience |
These selectors do not have testing purposes. So, the more the tests use them, the more fragile these tests become. They create problems because of class changes.
You need to use a dedicated attribute that will not change with other needs.This attribute is data-testid (or data-test, or data-cy, etc.).
The markup of the form must be changed with the new attribute.
The final form is taken from lines 73-119 in the Register.js file.
And the test is:
The test leverages dedicated attributes and selectors that do not change for non-testing purposes. The feedback in case of failure is comparable to the previous successful test.
The complete test is as follows:
Note: You can see the Cypress UI better by opening the link next to Your app can be found at:
context("Signup flow", () => {
it("The happy path should work", () => {
cy.visit("/register");
const random = Math.floor(Math.random() * 100000);
cy.get("[data-testid=username]").type(`Tester${random}`);
cy.get("[data-testid=email]").type(`user+${random}@realworld.io`);
cy.get("[data-testid=password]").type("mysupersecretpassword");
cy.get("[data-testid=signup-button]").click();
cy.get("[data-testid=no-articles-here]", { timeout: 10000 }).should("be.visible");
});
});
Unlike the previous version, this test helps us identify precisely what does not work, in case of failures related to the DOM elements.