隐式等待 vs 显示等待
隐式等待 | 显示等待 |
---|---|
全局声明,代码简洁 | 局部声明,代码复杂冗余 |
等待发生在webDriver控制浏览器时,不可控,仅适用于获取元素 | 等待发生在本地编写的代码逻辑,可以自定义等待获取条件 |
仅支持获取元素,找不到超时 | 也可以判断等待元素消失 |
返回结果是元素或超时异常 | 返回结果可以自定义 |
代码对比
/**
*隐式等待
*/
WebDriver driver = new FirefoxDriver();
//声明一次 告诉webDiver 获取元素时 最大等待时间10s
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.get("https://blog.csdn.net/zkf9044");
WebElement myDynamicElement = driver.findElement(By.id("uid"));
/**
*显示等待
*/
WebDriver driver = new FirefoxDriver();
driver.get("https://blog.csdn.net/zkf9044");
//创建一个等待对象,通过等待对象去执行的获取元素都是等待10秒
WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement myDynamicElement = wait.until(
ExpectedConditions.presenceOfElementLocated(By.id("uid"))//内置预期等待条件
);
以下是内置预期条件的API:https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/ExpectedConditions.html
自定义显示等待 预期条件
如果你不知道该怎么写,可以看看内置预期条件源码ExpectedConditions.presenceOfElementLocated(By.id("uid"))
源码很简单,阅读起来毫无压力
public static ExpectedCondition<WebElement> presenceOfElementLocated(
final By locator) {
return new ExpectedCondition<WebElement>() {
@Override
public WebElement apply(WebDriver driver) {
return findElement(locator, driver);
}
@Override
public String toString() {
return "presence of element located by: " + locator;
}
};
}
再看wait.until(ExpectedConditions.presenceOfElementLocated(By.id("uid")))
;
until方法的源码,他会执行Function
类 的 apply()
方法,ExpectedCondition
是Function
的子类;
继续分析 执行apply(),如果返回值不为空,本且是boolean类型时,返回结果为true则返回结果,否则睡眠0.5秒后再检查,如果等待的时间超过设置的最大等待时间则抛出异常timeoutException
public <V> V until(Function<? super T, V> isTrue) {
long end = clock.laterBy(timeout.in(MILLISECONDS));
Throwable lastException = null;
while (true) {
try {
V value = isTrue.apply(input);
if (value != null && Boolean.class.equals(value.getClass())) {
if (Boolean.TRUE.equals(value)) {
return value;
}
} else if (value != null) {
return value;
}
} catch (Throwable e) {
lastException = propagateIfNotIngored(e);
}
// Check the timeout after evaluating the function to ensure conditions
// with a zero timeout can succeed.
if (!clock.isNowBefore(end)) {
String message = messageSupplier != null ?
messageSupplier.get() : null;
String toAppend = message == null ?
" waiting for " + isTrue.toString() : ": " + message;
String timeoutMessage = String.format("Timed out after %d seconds%s",
timeout.in(SECONDS), toAppend);
throw timeoutException(timeoutMessage, lastException);
}
try {
sleeper.sleep(interval);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new WebDriverException(e);
}
}
}
根据源码分析可知,我们需要实现ExpectedCondition
接口重写apply
//预期条件 这个按钮没有被禁用
private static ExpectedCondition<WebElement> elementToBeClickable(
final By locator) {
//实现ExpectedCondition接口 返回 WebElement
return new ExpectedCondition<WebElement>() {
public ExpectedCondition<WebElement> visibilityOfElementLocated =
ExpectedConditions.visibilityOfElementLocated(locator);
@Override
public WebElement apply(WebDriver driver) {
//嵌套使用 检查元素是否可见 调用apply()
WebElement element = visibilityOfElementLocated.apply(driver);
try {
String isDisabled=element.getAttribute("disabled");
if (element != null && element.isEnabled() && StringUtils.isEmpty(isDisabled)) {
//element 不会空返回元素 否则返回null继续等待
return element;
} else {
return null;
}
} catch (Throwable e) {
return null;
}
}
@Override
public String toString() {
return "element to be clickable: " + locator;
}
};
}
//使用的时候
//我需要获取的按钮是没有被禁用的,才可点击
WebElement element= wait.until(elementToBeClickable(By.id("btn")));
element.click();
总结
使用显示等待最好,好的的东西除了“贵” 啥都好。