如何在Java中让Selenium-WebDriver等待几秒钟?


100

我正在研究Java Selenium-WebDriver。我加了

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

WebElement textbox = driver.findElement(By.id("textbox"));

因为我的应用程序需要几秒钟来加载用户界面。所以我设置了2秒的隐式等待。但是我找不到元素文本框

然后我添加 Thread.sleep(2000);

现在工作正常。哪一个是更好的方法?


Answers:


123

好吧,有两种类型的等待:显式和隐式等待。显式等待的想法是

WebDriverWait.until(condition-that-finds-the-element);

隐式等待的概念是

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

您可以在细节上有所不同 此处

在这种情况下,我宁愿使用显式等待(fluentWait尤其是):

public WebElement fluentWait(final By locator) {
    Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
            .withTimeout(30, TimeUnit.SECONDS)
            .pollingEvery(5, TimeUnit.SECONDS)
            .ignoring(NoSuchElementException.class);

    WebElement foo = wait.until(new Function<WebDriver, WebElement>() {
        public WebElement apply(WebDriver driver) {
            return driver.findElement(locator);
        }
    });

    return  foo;
};

fluentWait函数返回找到的Web元素。从文档开始fluentWait等待接口的实现,可以动态配置其超时和轮询间隔。每个FluentWait实例都定义了等待条件的最长时间,以及检查条件的频率。此外,用户可以将等待配置为在等待时忽略特定类型的异常,例如在页面上搜索元素时的NoSuchElementExceptions。 您可以在这里找到详细信息

fluentWait您的情况的用法如下:

WebElement textbox = fluentWait(By.id("textbox"));

恕我直言,这种方法更好,因为您不知道确切等待多少时间,并且可以在轮询间隔中设置任意时间值,通过该时间值可以验证元素是否存在。问候。


3
那是import com.google.common.base.Function;,不是import java.util.function.Function;
尚未核对

还没有检查,实际上是使用intelij IDEA进行开发,它正在帮助自动导入(在基于Maven的项目中工作时)
eugene.polschikov

1
我只是想告诉其他人,他们可能想知道Function正在使用哪个。起初对我来说并不明显。感谢你的回答。
haventcheck

1
或者,您可以只使用lambda表达式:driver -> driver.findElement(locator)。使用此方法时,您根本不需要导入语句。
Thibstars's

2
现在是: .withTimeout(Duration.ofSeconds(30)).pollingEvery(Duration.ofSeconds(5))代替: .withTimeout(30, TimeUnit.SECONDS).pollingEvery(5, TimeUnit.SECONDS)
SuperRetro

16

这个线程有点旧,但是以为我会发布我目前正在做的事情(正在进行中)。

尽管我仍然遇到系统负载沉重且单击提交按钮(例如,login.jsp)的情况,但所有三种情况(请参见下文)均会返回 true但下一页(例如home.jsp)却没有。还没有开始加载。

这是一个通用的等待方法,它采用ExpectedConditions的列表。

public boolean waitForPageLoad(int waitTimeInSec, ExpectedCondition<Boolean>... conditions) {
    boolean isLoaded = false;
    Wait<WebDriver> wait = new FluentWait<>(driver)
            .withTimeout(waitTimeInSec, TimeUnit.SECONDS)
            .ignoring(StaleElementReferenceException.class)
            .pollingEvery(2, TimeUnit.SECONDS);
    for (ExpectedCondition<Boolean> condition : conditions) {
        isLoaded = wait.until(condition);
        if (isLoaded == false) {
            //Stop checking on first condition returning false.
            break;
        }
    }
    return isLoaded;
}

我定义了各种可重用的ExpectedConditions(以下三个)。在此示例中,三个预期条件包括document.readyState ='complete',不存在“ wait_dialog”和不存在“ spinners”(指示异步数据的元素正在被请求)。

通常只有第一个可以应用于所有网页。

/**
 * Returns 'true' if the value of the 'window.document.readyState' via
 * JavaScript is 'complete'
 */
public static final ExpectedCondition<Boolean> EXPECT_DOC_READY_STATE = new ExpectedCondition<Boolean>() {
    @Override
    public Boolean apply(WebDriver driver) {
        String script = "if (typeof window != 'undefined' && window.document) { return window.document.readyState; } else { return 'notready'; }";
        Boolean result;
        try {
            result = ((JavascriptExecutor) driver).executeScript(script).equals("complete");
        } catch (Exception ex) {
            result = Boolean.FALSE;
        }
        return result;
    }
};
/**
 * Returns 'true' if there is no 'wait_dialog' element present on the page.
 */
public static final ExpectedCondition<Boolean> EXPECT_NOT_WAITING = new ExpectedCondition<Boolean>() {
    @Override
    public Boolean apply(WebDriver driver) {
        Boolean loaded = true;
        try {
            WebElement wait = driver.findElement(By.id("F"));
            if (wait.isDisplayed()) {
                loaded = false;
            }
        } catch (StaleElementReferenceException serex) {
            loaded = false;
        } catch (NoSuchElementException nseex) {
            loaded = true;
        } catch (Exception ex) {
            loaded = false;
            System.out.println("EXPECTED_NOT_WAITING: UNEXPECTED EXCEPTION: " + ex.getMessage());
        }
        return loaded;
    }
};
/**
 * Returns true if there are no elements with the 'spinner' class name.
 */
public static final ExpectedCondition<Boolean> EXPECT_NO_SPINNERS = new ExpectedCondition<Boolean>() {
    @Override
    public Boolean apply(WebDriver driver) {
        Boolean loaded = true;
        try {
        List<WebElement> spinners = driver.findElements(By.className("spinner"));
        for (WebElement spinner : spinners) {
            if (spinner.isDisplayed()) {
                loaded = false;
                break;
            }
        }
        }catch (Exception ex) {
            loaded = false;
        }
        return loaded;
    }
};

根据页面的不同,我可以使用其中一个或全部:

waitForPageLoad(timeoutInSec,
            EXPECT_DOC_READY_STATE,
            EXPECT_NOT_WAITING,
            EXPECT_NO_SPINNERS
    );

下列类中还预定义了ExpectedConditions: org.openqa.selenium.support.ui.ExpectedConditions


1
好答案!我从未见过有人通过方法构造函数传入ExpectedCondition项目。真是个好主意。+1
djangofan

杰夫·文森特,能否请您告诉我该页面的工作将等待5分钟,如果是,那么请建议需要发送哪个位置参数?
卡纳姆

这是一个很好的答案。我有类似的函数,但是,我必须在每个函数中都调用它,这样我的脚本才能等到页面加载(旋转器)。有没有办法我可以将这个waitForSpinner函数以静默方式挂接到ImplicitWait中,这样我就不必在函数中每次都调用它了?就像定义功能一样,一次将其挂接到驱动程序即可。
Bhuvanesh Mani

13

如果使用webdriverJs(node.js),

driver.findElement(webdriver.By.name('btnCalculate')).click().then(function() {
    driver.sleep(5000);
});

上面的代码使浏览器在单击按钮后等待5秒钟。


18
当问题专门针对Java而不是节点/ JavaScript时,为什么要发布此消息?这与回答如何在红宝石中做到这一点无关。
Thor84no

1
建议使用睡眠绝对不是一个好的解决方案
Kiril S.

@ Thor84no主要是因为我们某些正在搜索Web解决方案的人找到了这个答案
Kitanga Nday

10

使用Thread.sleep(2000);是无条件的等待。如果您的测试加载速度更快,您仍然需要等待。因此,原则上使用implicitlyWait是更好的解决方案。

但是,我不明白为什么implicitlyWait在您的情况下不起作用。您是否测量过findElement实际是否需要花费两秒钟才能引发异常。如果是这样,您可以尝试使用答案中所述的WebDriver的条件等待吗?


3

我喜欢使用自定义条件。这是Python中的一些代码:

def conditions(driver):
    flag = True
    ticker = driver.find_elements_by_id("textbox")
    if not ticker:
        flag = False
    return flag

... click something to load ...
self.wait = WebDriverWait(driver, timeout)
self.wait.until(conditions)

每当需要等待时,都可以通过检查某个元素的存在来明确地执行此操作(此类元素可能因页面而异)。find_elements_by_id返回列表-是否为空,您只需要检查即可。


2

点击似乎被阻止?-如果您使用的是WebDriverJS,这是另一种等待方式:

driver.findElement(webdriver.By.name('mybutton')).click().then(function(){
  driver.getPageSource().then(function(source) {
    console.log(source);
  });
});

上面的代码在单击按钮后等待下一页加载,然后获取下一页的源代码。


1
是什么使代码等待下一页加载?只是回调中的第一个调用是getPageSource吗?
2015年

1

隐式等待和Thread.sleep都仅用于同步..但是区别是我们可以使用隐式等待整个程序,但是Thread.sleep仅适用于单个代码。这里我的建议是在程序中使用隐式等待一次每当您的网页刷新时,意味着在那个时候使用Thread.sleep会更好:)

这是我的代码:

package beckyOwnProjects;

import java.util.concurrent.TimeUnit;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.interactions.Actions;

public class Flip {

    public static void main(String[] args) throws InterruptedException {
        WebDriver driver=new FirefoxDriver();
        driver.manage().window().maximize();
        driver.manage().timeouts().implicitlyWait(2, TimeUnit.MINUTES);
        driver.get("https://www.flipkart.com");
    WebElement ele=driver.findElement(By.cssSelector(".menu-text.fk-inline-block"));
    Actions act=new Actions(driver);
    Thread.sleep(5000);
    act.moveToElement(ele).perform();
    }

}

0

有时,隐式等待似乎被覆盖,等待时间缩短了。[@ eugene.polschikov]很好地记录了为什么。我在使用Selenium 2进行测试和编码时发现,隐式等待是不错的选择,但有时您必须显式等待。

最好避免直接调用线程进入睡眠状态,但是有时没有好的方法。但是,还有其他Selenium提供的等待选项会有所帮助。事实证明,waitForPageToLoadwaitForFrameToLoad尤其有用。


0

隐式等待:在隐式等待期间,如果Web驱动程序由于可用性而无法立即找到它,则WebDriver将等待上述时间,并且在指定的时间段内它将不会尝试再次查找该元素。指定的时间结束后,它将在上一次引发异常之前尝试再次搜索元素。默认设置为零。设置时间后,Web驱动程序将等待WebDriver对象实例的时间段。

显式等待:在某些情况下,特定元素的加载可能需要一分钟以上的时间。在那种情况下,您绝对不希望为隐式等待设置大量时间,就像这样做一样,浏览器将为每个元素等待相同的时间。为了避免这种情况,您可以仅在所需元素上单独放置一个时间。通过遵循此操作,浏览器的隐式等待时间对于每个元素来说都将很短,而对于特定元素来说则将很长。


0

有时隐式等待失败,说一个元素存在,但实际上不存在。

解决方案是避免使用driver.findElement并将其替换为隐式使用Explicit Wait的自定义方法。例如:

import org.openqa.selenium.NoSuchElementException;


public WebElement element(By locator){
    Integer timeoutLimitSeconds = 20;
    WebDriverWait wait = new WebDriverWait(driver, timeoutLimitSeconds);
    try {
        wait.until(ExpectedConditions.presenceOfElementLocated(locator));
    }
    catch(TimeoutException e){
        throw new NoSuchElementException(locator.toString());
    }
    WebElement element = driver.findElement(locator);
    return element;
}

除了偶发性的偶发性故障,还有其他原因可以避免隐式等待(请参阅此链接))。

您可以以与driver.findElement相同的方式使用此“ element”方法。例如:

    driver.get("http://yoursite.html");
    element(By.cssSelector("h1.logo")).click();

如果您真的只想等待几秒钟进行故障排除或其他罕见情况,则可以创建类似于Selenium IDE提供的暂停方法:

    public void pause(Integer milliseconds){
    try {
        TimeUnit.MILLISECONDS.sleep(milliseconds);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

0

:等待几秒钟,然后使用Selenium WebDriver进行元素可见性检查以下方法。

hiddenlyWait():WebDriver实例等待直到整个页面加载。您需要等待30到60秒才能等待整个页面加载。

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

ExplicitlyWait WebDriverWait():WebDriver实例等待直到完整页面加载。

WebDriverWait wait = new WebDriverWait(driver, 60);

wait.until(ExpectedConditions.visibilityOf(textbox));

driver.findElement(By.id("Year")).sendKeys(allKeys);

注意:请使用ExplicitlyWait WebDriverWait()处理任何特定的WebElement。



-1

我希望下面的代码等待2秒。

for(int i=0; i<2 && driver.findElements(By.id("textbox")).size()==0 ; i++){
   Thread.sleep(1000);
}

-1
Thread.sleep(1000);

更糟糕的是:作为静态等待,这会使测试脚本变慢。

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

这是一个动态的等待

  • 在webdriver存在之前一直有效,或者在驱动程序生存期之前具有作用域
  • 我们也可以隐式等待。

最后,我建议的是

WebDriverWait wait = new WebDriverWait(driver,20);
wait.until(ExpectedConditions.<different canned or predefined conditions are there>);

有一些预定义的条件:

isAlertPresent();
elementToBeSelected();
visibilityOfElementLocated();
visibilityOfAllElementLocatedBy();
frameToBeAvailableAndSwitchToIt();
  • 这也是动态等待
  • 在此等待仅需几秒钟
  • 我们必须对要使用的特定Web元素使用显式等待。

-4
Thread.Sleep(5000);

这确实对我有所帮助,但需要注意InterruptedException异常。所以最好用try and catch包围它:

try {
    Thread.Sleep(5000);
} catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

要么

添加引发声明:

public class myClass {
    public static void main(String[] args) throws InterruptedException
    { ... }

我希望使用第二个,因为这样一来便可以使用任意sleep()次数,try并且catch每次使用时sleep()都避免重复和阻塞。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.