使用Java编写的Selenium WebDriver测试中,waitForVisible/waitForElementPresent的相当之处是什么?

59

使用“HTML”Selenium测试(使用Selenium IDE或手动创建),您可以使用一些非常方便的命令,例如 WaitForElementPresent WaitForVisible 。请参考此处获取更多信息。

<tr>
    <td>waitForElementPresent</td>
    <td>id=saveButton</td>
    <td></td>
</tr>

当使用Java编写Selenium测试(Webdriver / Selenium RC,我不确定术语),是否有类似的内置功能

例如,检查需要一段时间才能打开的对话框是否可见...

WebElement dialog = driver.findElement(By.id("reportDialog"));
assertTrue(dialog.isDisplayed());  // often fails as it isn't visible *yet*

什么是最干净而且鲁棒的编写此类检查的方法?

随处添加Thread.sleep() 调用既丑陋又脆弱,而编写自己的while循环也显得相当笨拙...


你需要使用 WebDriverWait 类 - 请查看此链接以获取更多信息 - http://selenium.googlecode.com/svn/trunk/docs/api/java/index.html - Hari Reddy
6个回答

114

隐式等待和显式等待

隐式等待

隐式等待是告诉WebDriver,在尝试查找元素或一组元素时,如果它们不是立即可用的,则轮询DOM一段时间。默认设置为0。一旦设置,隐式等待将在WebDriver对象实例的生命周期内保持设置。

driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

显式等待 + 预期条件

显式等待是你所定义的代码,用于在继续执行代码之前等待某个条件发生。最糟糕的情况是Thread.sleep(),它将条件设置为精确的等待时间段。 提供了一些方便的方法来帮助你编写只需等待所需时间的代码。 WebDriverWait与ExpectedCondition组合使用是实现此目的的一种方式。

WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement element = wait.until(
        ExpectedConditions.visibilityOfElementLocated(By.id("someid")));

2
ExpectedConditions是一个类还是接口?如果是,应该导入哪个包? - Ripon Al Wasim
@RiponAlWasim 你可能已经知道了,但我会尽力回答所有问题... 如JavaDoc所示,它是一个包含静态方法的类,位于org.openqa.selenium.support.ui包中。 - Petr Janeček
那么隐式等待是否也适用于尝试点击但仍处于禁用状态的元素? 比如我点击一个按钮以使另一个按钮出现。然后我尝试点击这个其他按钮。然后WebDriver意识到这个其他按钮是禁用的。现在会发生什么?它会抛出异常还是等待当前驱动程序配置的秒数,然后再次尝试点击它? - Mercious
@Mercious 你可以轻松地自己尝试一下,但我会为你节省时间 - 它会抛出异常。按钮将被找到,因此WebDriver不会隐式等待它变为可用状态。你需要编写一个显式的等待来解决这个问题。 - Petr Janeček
1
@Slanec 如果测试很容易,我本来就会这样做了。当像这样的东西失败时,我几乎猜不出问题在哪里,因为我正在测试的网站还使用了很多混乱的JavaScript。我正在尝试弄清楚这一点。谢谢您澄清这个问题,这对我很有帮助。 - Mercious
@blaszard,你确定吗?我现在正在查看Javadocs,这个方法仍然存在。即使是API的alpha版本4.0.0,它也存在。你看的是什么? - Petr Janeček

10
WebElement myDynamicElement = (new WebDriverWait(driver, 10))
.until(ExpectedConditions.presenceOfElementLocated(By.id("myDynamicElement")));

这段代码会等待最多10秒钟,如果找到了元素则会在0-10秒内返回它。默认情况下,WebDriverWait每500毫秒调用ExpectedCondition一次,直到它成功返回。对于期望条件类型为布尔型的ExpectedCondition,成功返回指返回true;对所有其他期望条件类型,成功返回指返回非空值。

WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("someid")));

元素可点击 - 它被显示并启用。

引自WebDriver文档:显式和隐式等待


0
public static boolean waitForUntilVisible(WebDriver driver, Integer time, By by ) {
    WebDriverWait wait = new WebDriverWait(driver,  Duration.ofSeconds(time));

    try {
        wait.until( ExpectedConditions.presenceOfElementLocated(by) ); 
    }catch(NoSuchElementException e) {
        return false;
    }catch (TimeoutException e) {
        return false;
    }
    return true;
}

这段代码需要导入哪些模块? - Csaba Toth

0

其实问题在于您可能并不希望测试无限运行。您只是想在库决定元素不存在之前等待更长的时间。在这种情况下,最优雅的解决方案是使用隐式等待,它专门为此而设计:

driver.manage().timeouts().implicitlyWait( ... )

谢谢!这看起来很完美... 除了对于我的waitForVisible用例它不起作用。我想WebDriver在findElement()中应用超时,但是如果它立即成功返回并且isDisplayed随后因为它还没有可见而失败(请参见问题中的Java代码)怎么办? - Jonik
1
@Jonik:我之前在这里已经回答过类似的问题了:https://dev59.com/M1jUa4cB1Zd3GeqPQVVx#6379817。诀窍是使用`WebDriverWait`,你可以指定一个被轮询的回调函数。它类似于自己进行轮询,但是进行了抽象处理。它还处理了超时问题。那个答案中的代码需要稍作修改,但你应该能够理解思路。 - Mike Kwan
刚刚验证了一下,这确实可以作为waitForElementPresent的等效方法(直接使用)。谢谢,我明天会看看其他答案(现在太累了,想不清楚) :) - Jonik
事后提示:隐式等待会破坏ExpectedConditions,因为它们在内部使用driver.findElement。 - Hubert Grzeskowiak

0

另一种等待元素显示的方法是等待最多10秒钟的时间:

(new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() {
            public Boolean apply(WebDriver d) {
                return d.findElement(By.id("<name>")).isDisplayed();

            }
        });

-4

对于单个元素,可以使用以下代码:

private boolean isElementPresent(By by) {
        try {
            driver.findElement(by);
            return true;
        } catch (NoSuchElementException e) {
            return false;
        }
    }
for (int second = 0;; second++) {
            if (second >= 60){
                fail("timeout");
            }
            try {
                if (isElementPresent(By.id("someid"))){
                    break;
                }
                }
            catch (Exception e) {

            }
            Thread.sleep(1000);
        }

3
这段代码存在太多的问题,难以认真对待。1. 重新造轮子,查看其他答案以获取内置等待功能。2. Thread.sleep 代码难看且容易出错。3. 捕捉通用异常也是如此。4. "if" 语句没有花括号,这是常见错误的来源。5. "fail()" 是什么?最好在测试中抛出有意义的异常。6. 你将循环条件从 "for" 语句中提取出来了。7. 将多个语句写在一行中可能看起来很酷但实际上不是。此外,这篇文章的缩进也有问题。 - Hubert Grzeskowiak
2
我同意,最好使用显式等待(WebDriverWait)。使用Thread.sleep()不太好。根据您的评论,我已经在if语句中使用花括号重新组织了代码,以使其更易读。感谢您的评论。 - Ripon Al Wasim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接