Selenium—这样混合隐式等待和显式等待可以吗?
下面是一个(来自“Lets Kode It”)用selenium和Java开发web自动化框架的例子但是,这不是一个java问题。您只需要了解这些语言中任何一种语言的selenium—javascript、python、ruby、c#和java 讲师开发了一个CustomDriver类,其方法/功能如下所示。该方法等待元素可单击,而不必在代码中的任何地方编写Selenium—这样混合隐式等待和显式等待可以吗?,selenium,selenium-webdriver,webdriverwait,Selenium,Selenium Webdriver,Webdriverwait,下面是一个(来自“Lets Kode It”)用selenium和Java开发web自动化框架的例子但是,这不是一个java问题。您只需要了解这些语言中任何一种语言的selenium—javascript、python、ruby、c#和java 讲师开发了一个CustomDriver类,其方法/功能如下所示。该方法等待元素可单击,而不必在代码中的任何地方编写WebDriverWait语句。它首先将隐式等待设置为零,执行显式等待,然后将隐式等待设置为框架中使用的原始值 这种方法对我来说似乎没问题,
WebDriverWait
语句。它首先将隐式等待设置为零,执行显式等待,然后将隐式等待设置为框架中使用的原始值
这种方法对我来说似乎没问题,但我不确定。这样混合使用隐式和显式等待会导致任何问题吗
更新(2020年3月24日)-我已经知道混合隐式和显式等待被认为是一种不好的做法,因为它可能导致不可预测的等待时间。我不是在问不可预测的等待时间,因为已经有很多关于这方面的问题和文章了
相反,我要问的是,如果隐式等待在每次执行显式等待之前都设置为零,那么可以吗?这还会导致不可预测的等待问题吗?会不会引起其他问题
/*
Click element when element is clickable
@param locator - locator strategy, id=>example, name=>example, css=>#example,
tag=>example, xpath=>//example, link=>example
@param timeout - Duration to try before timeout
*/
public void clickWhenReady(By locator, int timeout) {
try {
driver.manage().timeouts().implicitlyWait(0, TimeUnit.SECONDS);
WebElement element = null;
System.out.println("Waiting for max:: " + timeout + " seconds for element to be clickable");
WebDriverWait wait = new WebDriverWait(driver, 15);
element = wait.until(
ExpectedConditions.elementToBeClickable(locator));
element.click();
System.out.println("Element clicked on the web page");
driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
} catch (Exception e) {
System.out.println("Element not appeared on the web page");
driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
}
}
我不建议把它们混在一起。As
隐式等待
通常在WebDriver系统的远程
端实现,这意味着它们在基于浏览器的驱动程序(例如:chromedriver.exe、IEDriverServer.exe)中处理,其中As显式等待
在本地语言绑定(如Java、ruby、python等)上实现
下面是使用远程服务器运行脚本时发生的典型示例
本地代码->远程服务器->远程服务器上的本地语言绑定->chromedriver.exe或IEDriverServer.exe等远程组件。如果涉及到网格,事情会变得更加复杂,因为它可能是链之间的另一层。
因此,当您混合使用隐式和显式等待时,最终可能会出现未定义的行为。此外,由于隐式等待是在驱动程序级别实现的,因此它们可能随时更改,并对脚本产生影响。所以,坚持显式等待和完全控制总是更好的
使用当前的技术,元素可能在元素出现后不久才渲染。因此,使用隐式等待
是不够的,因此强烈建议使用显式等待
。可能存在一些边缘情况,我们可能必须使用隐式等待
,但是,如果您计划将来扩展脚本以在网格上运行/使用远程服务器,请不要将这两种情况混合使用。使用隐式等待和显式等待的问题归结为在Selenium源代码中如何实现ExpectedConditions
的缺陷
让我通过分析以下代码来解释问题:
WebDriver driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
WebElement element = new WebDriverWait(driver, 5, 1000).until(ExpectedConditions.elementToBeClickable(By.id("some_id")));
我们初始化驱动程序并将隐式等待设置为10秒。这意味着driver.findElement()
将等待10秒,直到找到所有元素,然后抛出NoTouchElementException
这是一个非常重要的注意事项
然后我们设置了一个显式等待,持续时间为5秒,轮询之间的睡眠时间为1000毫秒(1秒)。这意味着WebDriverWait
将每隔1秒到5秒轮询ExpectedConditions
为true。如果ExpectedConditions
返回true,轮询将停止,并返回ExpectedConditions
中指定的对象。在上面的示例代码中,返回的对象是WebElement
。如果ExpectedConditions
在5秒后为false,则抛出TimeoutException
(或我们预期的那样)。现在是时候看看ExpectedConditions
中发生了什么
ExpectedConditions.elementToBeClickable()
代码具有以下语法
public static ExpectedCondition<WebElement> elementToBeClickable(final By locator) {
return new ExpectedCondition<WebElement>() {
@Override
public WebElement apply(WebDriver driver) {
WebElement element = visibilityOfElementLocated(locator).apply(driver);
try {
if (element != null && element.isEnabled()) {
return element;
}
return null;
} catch (StaleElementReferenceException e) {
return null;
}
}
};
}
5.注意上面在visibilityOfElementLocated()
方法中如何调用driver.findElement(locator)
。如果未找到该元素,将应用10秒的隐式等待。因此,驱动程序将等待10秒钟,直到它抛出NoTouchElementException
但是等等(双关语不是故意的)!在elementtobelickable()
条件下,我们的显式等待不是设置为5秒超时吗?是的,但将首先应用隐式等待。WebDriverWait将捕获NoTouchElementException
,并在10秒后抛出TimeoutException
,而不是设置5秒的显式等待。这就是问题解决方案试图解决的问题。该解决方案尝试将隐式等待设置为0秒,以便正确执行显式等待条件,然后重置隐式等待
问题中提供的实现只需要一个细节就可以完成任务。隐式等待硬编码为3秒,这并不理想。如何将隐式等待作为全局通用常量提供是非常具体的。我们正在对驱动程序设置隐式等待,并且可以预期隐式等待,如“driver”driver.manage().timeout().getImplicitWait()
。虽然理想,但不幸的是,这不可能直接实现。这里有一些解决方法,@forresthopkinsa在创建扩展驱动程序以获得隐式等待方面有着相当的优势
更新(2020年3月24日)
问:这种方法对我来说似乎不错,但我不确定。这样混合使用隐式和显式等待会导致任何问题吗
我在问,如果隐式等待在每次执行显式等待之前都设置为零,那么可以吗?这还会导致不可预测的等待问题吗?它会导致其他问题吗?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 e) {
return null;
}
}
};
}