如何用WebElement替代By实现PresenceOfAllElementsLocatedBy?及PageFactory元素等待问题
Hey there! Let's break down solutions for your two Selenium scenarios, tailored for PageFactory and explicit wait workflows:
PresenceOfAllElementsLocatedBy with WebElement Instead of By The built-in ExpectedConditions.presenceOfAllElementsLocatedBy() relies on a By locator, but if you're working with WebElement instances initialized via PageFactory, you can create a custom ExpectedCondition to check for the presence of all elements in a list.
PageFactory uses proxy objects for WebElement, so actual element lookup only happens when you call a method like isDisplayed() — this makes the custom condition feasible:
import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.ExpectedCondition; import java.util.ArrayList; import java.util.List; public class CustomExpectedConditions { public static ExpectedCondition<List<WebElement>> presenceOfAllWebElements(final List<WebElement> elements) { return new ExpectedCondition<>() { @Override public List<WebElement> apply(WebDriver driver) { List<WebElement> presentElements = new ArrayList<>(); for (WebElement element : elements) { try { // Adjust the check based on your needs (isDisplayed(), isEnabled(), etc.) if (element.isDisplayed()) { presentElements.add(element); } } catch (Exception ignored) { // Ignore exceptions for elements that aren't yet present } } // Return the list only when all elements are present; else return null to keep waiting return presentElements.size() == elements.size() ? presentElements : null; } @Override public String toString() { return "presence of all target web elements"; } }; } }
Usage with PageFactory:
If your page class has a list of elements initialized via @FindBy:
@FindBy(css = ".product-item") private List<WebElement> productItems; // In your wait logic: new FluentWait<>(driver) .withTimeout(10, TimeUnit.SECONDS) .pollingEvery(50, TimeUnit.MILLISECONDS) .until(CustomExpectedConditions.presenceOfAllWebElements(productItems));
Your existing FluentWait setup is on the right track — let's refine it to handle both By locators and PageFactory WebElement instances:
Option 1: Wait for element absence using By
This is straightforward with built-in ExpectedConditions:
import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.FluentWait; import java.util.concurrent.TimeUnit; public void waitForElementToDisappear(By by) { try { FluentWait<WebDriver> wait = new FluentWait<>(driver) .withTimeout(10, TimeUnit.SECONDS) .pollingEvery(50, TimeUnit.MILLISECONDS) .ignoring(NoSuchElementException.class) .ignoring(StaleElementReferenceException.class); // Wait until the element is no longer present in the DOM wait.until(ExpectedConditions.not(ExpectedConditions.presenceOfElementLocated(by))); } catch (TimeoutException e) { throw new AssertionError("Element still exists after timeout: " + by); } }
Option 2: Wait for PageFactory WebElement to disappear
For elements initialized via PageFactory, we can check by attempting to interact with the element and catching exceptions that signal absence:
import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.FluentWait; import java.util.concurrent.TimeUnit; public void waitForWebElementToDisappear(final WebElement element) { try { FluentWait<WebDriver> wait = new FluentWait<>(driver) .withTimeout(10, TimeUnit.SECONDS) .pollingEvery(50, TimeUnit.MILLISECONDS) .ignoring(NoSuchElementException.class) .ignoring(StaleElementReferenceException.class); wait.until(new ExpectedCondition<Boolean>() { @Override public Boolean apply(WebDriver driver) { try { // If the element is still present, this call will succeed — return false to keep waiting element.isDisplayed(); return false; } catch (Exception e) { // Exceptions mean the element is gone — return true to end the wait return true; } } }); } catch (TimeoutException e) { throw new AssertionError("WebElement still exists after timeout"); } }
Key Notes:
- If you need to wait for an element to be invisible (not just absent from DOM), use
ExpectedConditions.invisibilityOfElementLocated(by)orExpectedConditions.invisibilityOf(element)— this handles both hidden and absent elements. - Avoid ignoring
TimeoutExceptionintentionally; letting it bubble up (or wrapping it in an assertion error) helps catch cases where elements don't disappear as expected.
内容的提问来源于stack exchange,提问作者arena

