Choosing locators and waits that keep tests stable
How to pick reliable locators with id, CSS and XPath, and how to replace sleeps with explicit waits to stop flaky tests.
Most failing Selenium tests are not caused by bugs in the application. They fail because a locator broke or the test ran before the page was ready. Getting locators and waits right fixes most flakiness.
Choosing a locator
A practical order of preference:
- id, if it is unique and not generated randomly:
By.id("email"). - name or a dedicated test attribute:
By.cssSelector("[data-test='submit']"). - CSS selector for most other cases:
input[name='email'],.cart .total. - XPath when you need text or to move up the tree:
//button[text()='Submit'].
Avoid absolute XPath such as /html/body/div[2]/form/input[3]. Any layout change breaks it. Also be wary of ids with numbers that change on every load, such as input-8231.
Useful XPath patterns
//a[contains(@href, 'invoice')]
//label[text()='Email']/following-sibling::input
//tr[td[text()='Order 42']]//buttonRemember that text() reads text while @name reads an attribute, and that // searches anywhere below while a single / means a direct child.
Implicit and explicit waits
An implicit wait is set once and makes every element lookup retry until the element exists or the time runs out. It cannot wait for an element to become visible or clickable.
An explicit wait waits for a specific condition:
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement pay = wait.until(
ExpectedConditions.elementToBeClickable(By.id("pay")));
pay.click();It continues as soon as the condition is true, so it is both faster and more reliable than a fixed delay. Do not mix implicit and explicit waits; the combined timing becomes unpredictable. Many teams set the implicit wait to zero and use explicit waits everywhere.
Why Thread.sleep is a problem
A fixed sleep always waits the full time, slowing the suite, and still fails when the application is slower than expected on a busy test server. Replace each sleep with a wait for the real condition: visible, clickable, text present, or a loading spinner gone.
Habit to build: when a test fails intermittently, ask two questions first. Is the locator unique and stable, and is the test waiting for the right condition?
More in Software testing