Best Practices
This guide aims to share our best practices that help you write performant and resilient tests.
Use resilient selectors
Using selectors that are resilient to changes in the DOM, you'll have a less or even no tests failing when the for example a class is removed from an element.
Classes can be applied to multiple elements and should be avoided if possible unless you deliberately want to fetch all elements with that class.
// 👎
await $('.button');
All these selectors should return a single element.
// 👍
await $('aria/Submit');
await $('[test-id="submit-button"]');
await $('#submit-button');
Note: To find out all the possible selectors WebdriverIO supports, checkout our Selectors page.
Limit the amount of element queries
Every time you use the $
or $$
command (this includes chaining them), WebdriverIO tries to locate the element in the DOM. These queries are expensive so you should try to limit them as much as possible.
Queries three elements.
// 👎
await $('table').$('tr').$('td');
Queries only one element.
// 👍
await $('table tr td');
The only time you should use chaining is when you want to combine different selector strategies. In the example we use the Deep Selectors, which is a strategy to go inside the shadow DOM of an element.
// 👍
await $('custom-datepicker').$('#calendar').$('aria/Select');
Prefer locating a single element instead of taking one from a list
It isn't always possible to do this but using CSS pseudo-classes like :nth-child you can match elements based on the indexes of the elements in the child list of their parents.
Queries all table rows.
// 👎
await $$('table tr')[15];
Queries a single table row.
// 👍
await $('table tr:nth-child(15)');