The following test script works correctly if the site is fast:
@Test
public void searchReturnsResultsTest1() {
driver.get(URL);
Assert.assertTrue(driver.getCurrentUrl().contains(URL));
WebElement searchBox = driver.findElement(searchBoxName);
searchBox.sendKeys("iphone");
WebElement searchButton = driver.findElement(searchButtonXpath);
searchButton.click();
}
If the site is slow, then the script can have all sorts of issues.
If the home page is not loaded fast, it is possible that the assertion fails since driver.getCurrentUrl() may return an empty value.
If the content of the home page is not loaded quickly, it is possible that finding search box will not work. Or if search box can be found, typing into it may not work if search box is not enabled at that time.
The test script obviously needs some synchronization with the site.
This synchronization means that the script should wait until the page is loaded so there is a page url available. Then, the script should wait until search box is enabled before typing into it. Also, the script should wait until search button is enabled before clicking it.
We can implement our own method for waiting until the page url is correct:
private void waitUntilUrlContains(String url) throws InterruptedException {
int counter = 0;
while (counter < 10) {
if (driver.getCurrentUrl().contains(url))
return;
Thread.sleep(1000);
counter++;
}
throw new RuntimeException("url is not correct!");
}
The method is very straightforward.
It checks if the current page url contains some other url (the method’s parameter). If this works, the method ends. If it does not work, the method will try a few more times, with 1 second delay between retries. If the url is still not correct after 10 retries, the method generates an exception.
We can also create a similar method for waiting until an element is enabled:
private void waitUntilElementClickable(By by) throws InterruptedException {
int counter = 0;
while (counter < 10) {
try {
if (driver.findElement(by).isEnabled())
return;
}
catch (NoSuchElementException e) {
Thread.sleep(1000);
counter++;
}
}
throw new RuntimeException("element is not found!");
}
With these wait methods, the test script becomes:
@Test
public void searchReturnsResultsTest2() throws InterruptedException {
driver.get(URL);
waitUntilUrlContains(URL);
Assert.assertTrue(driver.getCurrentUrl().contains(URL));
waitUntilElementClickable(searchBoxName);
WebElement searchBox = driver.findElement(searchBoxName);
searchBox.sendKeys("iphone");
waitUntilElementClickable(searchButtonXpath);
WebElement searchButton = driver.findElement(searchButtonXpath);
searchButton.click();
}
The test script is much more stable even if the site is slow.
But, the wait methods are just not good.
Why are they not good?
Let’s see.
Assume that your tests need later to wait until the page title is correct. Then, we need another method named waitUntilTitleContains().
Then, we need to wait until an element is found or visible or it has an attribute or an attribute contains a value or the element contains a text ….
Slowly, from 2 wait methods, we will go to 20 methods.
And unfortunately, none of them is well implemented.
And,
they duplicate functionality that is already available out of box in the Selenium library.
So do not create these methods.
Instead, use the WebDriverWait and ExpectedConditions classes from the Selenium library to achieve the same behavior:
@Test
public void searchReturnsResultsTest3() throws InterruptedException {
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
driver.get(URL);
wait.until(ExpectedConditions.urlContains(URL));
Assert.assertTrue(driver.getCurrentUrl().contains(URL));
wait.until(ExpectedConditions.elementToBeClickable(searchBoxName));
WebElement searchBox = driver.findElement(searchBoxName);
searchBox.sendKeys("iphone");
wait.until(ExpectedConditions.elementToBeClickable(searchButtonXpath));
WebElement searchButton = driver.findElement(searchButtonXpath);
searchButton.click();
}
This is what you should do, use classes that are already in the library instead of creating your own classes.
ExpectedConditions has lots of common conditions, more than 25, that work for
pages
single element
multiple elements
windows
frames
javascript
Do not “re-invent the wheel”, use the existing one.