public
Last active

Exercise on safely waiting for unstable web elements with WebDriver.

  • Download Gist
endorsedmethod.java
Java
1 2 3 4 5 6 7 8
// This is the official Selenium documention endorsed method of waiting for elements.
// This method is ineffective because it still suffers from
// the stale element exception.
public static void clickByLocator ( final By locator ) {
WebElement myDynamicElement = ( new WebDriverWait(driver, 10))
.until( ExpectedConditions.presenceOfElementLocated( locator ) );
myDynamicElement.click();
}
method1.java
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// I gleamed this method from the Selenium Google forum.
// The .ignoring fluent method was a huge hint
// to the eventual solution I found but it still didn't work
// because I needed the ExpectedCondition apply method to give
// me some kind of message on failures.
public void waitForElementPresent(final By by, int timeout){
WebDriverWait wait = (WebDriverWait)new WebDriverWait(driver,timeout)
.ignoring(StaleElementReferenceException.class);
wait.until(new ExpectedCondition<Boolean>(){
@Override
public Boolean apply(WebDriver webDriver) {
WebElement element = webDriver.findElement(by);
return element != null && element.isDisplayed();
}
});
}
method2.java
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
// This was my breakthrough. My first partially working code.
public static void clickByLocator( final By locator ) {
staticlogger.info( "Click by locator: " + locator.toString() );
final long startTime = System.currentTimeMillis();
driver.manage().timeouts().implicitlyWait( 5, TimeUnit.SECONDS );
Wait<WebDriver> wait = new FluentWait<WebDriver>( driver )
.withTimeout(90000, TimeUnit.MILLISECONDS)
.pollingEvery(5500, TimeUnit.MILLISECONDS);
//.ignoring( StaleElementReferenceException.class );
wait.until( new ExpectedCondition<Boolean>() {
@Override
public Boolean apply( WebDriver webDriver ) {
try {
webDriver.findElement( locator ).click();
return true;
} catch ( StaleElementReferenceException e ) {
staticlogger.info( e.getMessage() + "\n");
staticlogger.info("Trying again...");
return false;
}
}
} );
driver.manage().timeouts().implicitlyWait( DEFAULT_IMPLICIT_WAIT, TimeUnit.SECONDS );
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
staticlogger.info("Finished click after waiting for " + totalTime + " milliseconds.");
}
method3.java
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
// An extension of method3 although I am unsure if I trust it
// because it re-gets the element AND the element gets assigned
// again after returning from the method. A few opportunities
// to go stale.
public static WebElement getElementByLocator( final By locator ) {
staticlogger.info( "Get element by locator: " + locator.toString() );
final long startTime = System.currentTimeMillis();
driver.manage().timeouts().implicitlyWait( 5, TimeUnit.SECONDS );
Wait<WebDriver> wait = new FluentWait<WebDriver>( driver )
.withTimeout(90000, TimeUnit.MILLISECONDS)
.pollingEvery(5500, TimeUnit.MILLISECONDS);
wait.until( new ExpectedCondition<Boolean>() {
@Override
public Boolean apply( WebDriver webDriver ) {
try {
webDriver.findElement( locator ).getTagName();
return true;
} catch ( StaleElementReferenceException e ) {
staticlogger.info( e.getMessage() + "\n");
staticlogger.info("Trying again for availability of element...");
return false;
}
}
} );
WebElement we = null;
try {
we = driver.findElement( locator ); // is this error prone?
} catch ( StaleElementReferenceException e ) {
staticlogger.info( "Stale element: \n" + e.getMessage() + "\n");
}
driver.manage().timeouts().implicitlyWait( DEFAULT_IMPLICIT_WAIT, TimeUnit.SECONDS );
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
staticlogger.info("Finished click after waiting for " + totalTime + " milliseconds.");
return we;
}
method4.java
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
// I realized that the try-catch doesn't really need to be within
// the ExpectedCondition final block. By moving the try-catch outside
// I would have access to the WebElement returned from findElement().
// So, I can create my own Boolean expected condition while I loop to
// hopefully accomplish a similar thing as method3.
public static WebElement getElementByLocator( By locator ) {
staticlogger.info( "Get element by locator: " + locator.toString() );
long startTime = System.currentTimeMillis();
driver.manage().timeouts().implicitlyWait( 9, TimeUnit.SECONDS );
WebElement we = null;
boolean unfound = true;
int tries = 0;
while ( unfound && tries < 20 ) {
tries += 1;
staticlogger.info("Locating remaining time: " + (180-(9*(tries-1) )) + " seconds." );
try {
we = driver.findElement( locator );
unfound = false; // FOUND IT
} catch ( StaleElementReferenceException ser ) {
staticlogger.info( "ERROR: Stale element. " + locator.toString() );
unfound = true;
} catch ( NoSuchElementException nse ) {
staticlogger.info( "ERROR: No such element. " + locator.toString() );
unfound = true;
} catch ( Exception e ) {
staticlogger.info( e.getMessage() );
}
}
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
staticlogger.info("Finished click after waiting for " + totalTime + " milliseconds.");
driver.manage().timeouts().implicitlyWait( DEFAULT_IMPLICIT_WAIT, TimeUnit.SECONDS );
return we;
}
method5.java
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
// Another approach, after everything I have learned, that might
// also be effective. With this method, a wait timeout occurs 3
// times within the 90 second limit. So, the method will run
// between 15-90 seconds, depending on the situation of failure.
public static WebElement getElementByLocator( final By locator ) {
LOGGER.info( "Get element by locator: " + locator.toString() );
final long startTime = System.currentTimeMillis();
Wait<WebDriver> wait = new FluentWait<WebDriver>( driver )
.withTimeout(30, TimeUnit.SECONDS)
.pollingEvery(5, TimeUnit.SECONDS)
.ignoring( StaleElementReferenceException.class ) ;
int tries = 0;
boolean found = false;
WebElement we = null;
while ( (System.currentTimeMillis() - startTime) < 91000 ) {
LOGGER.info( "Searching for element. Try number " + (tries++) );
try {
we = wait.until( ExpectedConditions.presenceOfElementLocated( locator ) );
found = true;
break;
} catch ( StaleElementReferenceException e ) {
LOGGER.info( "Stale element: \n" + e.getMessage() + "\n");
}
}
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
if ( found ) {
LOGGER.info("Found element after waiting for " + totalTime + " milliseconds." );
} else {
LOGGER.info( "Failed to find element after " + totalTime + " milliseconds." );
}
return we;
}
method6.java
Java
1 2 3 4
// create a demonstration of using a Closure instead of the typical inner class
// pattern for @Override that you see above in method #3
 
wait.until( webDriver -> webDriver.findElement( By.id( "foo" ) ) );
method7.java
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
.withTimeout( 30, TimeUnit.SECONDS )
.pollingEvery( 5, TimeUnit.SECONDS )
.ignoring( NoSuchElementException.class, StaleElementReferenceException.class );
// using a customized expected condition
WebElement foo1 = wait.until(new Function<WebDriver, WebElement>() {
public WebElement apply(WebDriver driver) {
return driver.findElement(By.id("foo1"));
}
});
// using a built-in expected condition
WebElement foo2 = wait.until( ExpectedConditions
.presenceOfElementLocated( By.id("foo2") ) );
// careful with this next one. it requires visibility attribute on html tag
WebElement foo3 = wait.until( ExpectedConditions
.visibilityOfElementLocated( By.id("foo3") ) );

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.