Which expected condition should you use? visibilityOf() or visibilityOfElementLocated?
One thing that you notice when checking all out-of-the-box expected conditions included in the Selenium library is that many of them have 2 versions:
one method that takes a WebElement parameter
public static ExpectedCondition<WebElement> visibilityOf(final WebElement element)
another method that takes a By parameter
public static ExpectedCondition<WebElement> visibilityOfElementLocated(final By locator)
Which one should you use?
It is useful to look at their implementations.
visibilityOf() is below:
public static ExpectedCondition<WebElement> visibilityOf(
final WebElement element) {
return new ExpectedCondition<WebElement>() {
@Override
public WebElement apply(WebDriver driver) {
return elementIfVisible(element);
}
@Override
public String toString() {
return "visibility of " + element;
}
};
}
private static WebElement elementIfVisible(WebElement element) {
return element.isDisplayed() ? element : null;
}
When visibilityOf() is used with an explicit wait,
WebElement element = driver.findElement(by);
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
wait.until(ExpectedConditions.visibilityOf(element));
the wait will keep executing the ExpectedCondition until the element is visible (notice that visibilityOf() uses elementIfVisible(element) for this purpose) or the timeout of the wait is exceeded.
The element is found before the wait is used.
The element finding happens only once.
If the element changes while the wait applies the expected condition, a stale element exception may be generated since the element is no longer the same. This exception is not caught or managed.
visibilityOfElementLocated() is next:
public static ExpectedCondition<WebElement> visibilityOfElementLocated(
final By locator) {
return new ExpectedCondition<WebElement>() {
@Override
public WebElement apply(WebDriver driver) {
try {
return elementIfVisible(driver.findElement(locator));
}
catch (StaleElementReferenceException |
NoSuchElementException e) {
//Returns null because element no longer/not present in DOM
return null;
}
}
@Override
public String toString() {
return "visibility of element located by " + locator;
}
};
}
private static WebElement elementIfVisible(WebElement element) {
return element.isDisplayed() ? element : null;
}
When visibilityOfElementLocated() is used with an explicit wait,
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
wait.until(ExpectedConditions.visibilityOfElementLocated(by));
the wait will keep executing the ExpectedCondition until the element is visible (notice that visibilityOfElementLocated() uses elementIfVisible(driver.findElement(locator)) for this purpose) or the timeout of the wait is exceeded.
The element is re-found every time the wait tries the expected condition.
If the element changes while the wait applies the expected condition, the try/catch statement from the apply() method catches the stale element exception and returns null so that the condition can be attempted again a bit later.
Let's summarize the 2 conditions:
visibilityOf()
uses a web element parameter
the web element is found only once before the wait is used
there is no handling of a stale element exception
visibilityOfElementLocated()
uses a by parameter
the element is found every time the wait applies the condition
it handles the stale element exception
This should make it easy to see how visibilityOfElementLocated() is a better option.
Thanks for reading.