I came across the problem described in Checking If alert exists before switching to it. I find horrible to capture a NullPointerException
. Has anyone solved this problem more elegantly?
My current solution uses a wait that captures the NPE. The client code just have to invoke waitForAlert(driver, TIMEOUT)
:
/**
* If no alert is popped-up within <tt>seconds</tt>, this method will throw
* a non-specified <tt>Throwable</tt>.
*
* @return alert handler
* @see org.openqa.selenium.support.ui.Wait.until(com.google.common.base.Function)
*/
public static Alert waitForAlert(WebDriver driver, int seconds) {
Wait<WebDriver> wait = new WebDriverWait(driver, seconds);
return wait.until(new AlertAvailable());
}
private static class AlertAvailable implements ExpectedCondition<Alert> {
private Alert alert = null;
@Override
public Alert apply(WebDriver driver) {
Alert result = null;
if (null == alert) {
alert = driver.switchTo().alert();
}
try {
alert.getText();
result = alert;
} catch (NullPointerException npe) {
// Getting around https://groups.google.com/d/topic/selenium-users/-X2XEQU7hl4/discussion
}
return result;
}
}
JavaDoc for FluentWait.until()
Repeatedly applies this instance's input value to the given function until one of the following occurs:
- the function returns neither null nor false,
- the function throws an unignored exception,
- the timeout expires
.......(snip)
Since NullPointerException
denotes a false condition, and WebDriverWait
is only ignoring NotFoundException
, just remove the try/catch block. An unchecked, unignored Exception
thrown in apply()
is semantically equivalent to returning null
as in your existing code.
private static class AlertAvailable implements ExpectedCondition<Alert> {
@Override
public Alert apply(WebDriver driver) {
Alert result = driver.switchTo().alert();
result.getText();
return result;
}
}
Based upon @Joe Coder answer, a simplified version of this wait would be:
/**
* If no alert is popped-up within <tt>seconds</tt>, this method will throw
* a non-specified <tt>Throwable</tt>.
*
* @return alert handler
* @see org.openqa.selenium.support.ui.Wait.until(com.google.common.base.Function)
*/
public static Alert waitForAlert(WebDriver driver, int seconds) {
Wait<WebDriver> wait = new WebDriverWait(driver, seconds)
.ignore(NullPointerException.class);
return wait.until(new AlertAvailable());
}
private static class AlertAvailable implements ExpectedCondition<Alert> {
@Override
public Alert apply(WebDriver driver) {
Alert alert = driver.switchTo().alert();
alert.getText();
return alert;
}
}
I have written a short test to proof the concept:
import com.google.common.base.Function;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.support.ui.Wait;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestUntil {
private static Logger log = LoggerFactory.getLogger(TestUntil.class);
@Test
public void testUnit() {
Wait<MyObject> w = new FluentWait<MyObject>(new MyObject())
.withTimeout(30, TimeUnit.SECONDS)
.ignoring(NullPointerException.class);
log.debug("Waiting until...");
w.until(new Function<MyObject, Object>() {
@Override
public Object apply(MyObject input) {
return input.get();
}
});
log.debug("Returned from wait");
}
private static class MyObject {
Iterator<Object> results = new ArrayList<Object>() {
{
this.add(null);
this.add(null);
this.add(new NullPointerException("NPE ignored"));
this.add(new RuntimeException("RTE not ignored"));
}
}.iterator();
int i = 0;
public Object get() {
log.debug("Invocation {}", ++i);
Object n = results.next();
if (n instanceof RuntimeException) {
RuntimeException rte = (RuntimeException)n;
log.debug("Throwing exception in {} invocation: {}", i, rte);
throw rte;
}
log.debug("Result of invocation {}: '{}'", i, n);
return n;
}
}
}
In this code, in the until
the MyObject.get()
is invoked four times. The third time, it throws an ignored exception, but the last one throws a not-ignored exception, interrupting the wait.
The output (simplified for readability):
Waiting until...
Invocation 1
Result of invocation 1: 'null'
Invocation 2
Result of invocation 2: 'null'
Invocation 3
Throwing exception in 3 invocation: java.lang.NullPointerException: NPE ignored
Invocation 4
Throwing exception in 4 invocation: java.lang.RuntimeException: RTE not ignored
------------- ---------------- ---------------
Testcase: testUnit(org.lila_project.selenium_tests.tmp.TestUntil): Caused an ERROR
RTE not ignored
java.lang.RuntimeException: RTE not ignored
at org.lila_project.selenium_tests.tmp.TestUntil$MyObject$1.<init>(TestUntil.java:42)
at org.lila_project.selenium_tests.tmp.TestUntil$MyObject.<init>(TestUntil.java:37)
at org.lila_project.selenium_tests.tmp.TestUntil$MyObject.<init>(TestUntil.java:36)
at org.lila_project.selenium_tests.tmp.TestUntil.testUnit(TestUntil.java:22)
Note that as the RuntimeException
is not ignored, the "Returned from wait" log is not printed.