Selenium等待文档准备就绪

151

有人能告诉我如何使Selenium等待页面完全加载吗? 我想要一些通用的东西,我知道我可以配置WebDriverWait并调用类似于“find”的内容来等待,但我不需要那么多。 我只需要测试页面成功加载并继续下一页进行测试。

我在.net中找到了一些东西,但无法在java中使其工作...

IWait<IWebDriver> wait = new OpenQA.Selenium.Support.UI.WebDriverWait(driver, TimeSpan.FromSeconds(30.00));
wait.Until(driver1 => ((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState").Equals("complete"));

有任何想法吗?


为什么你不想使用wait? - Amey
8
你是指显式等待吗?这需要花费时间,我正在测试大约10,000个页面。 - Girish
2
我的意思是,如果我正在测试大量的链接,添加一个固定的等待时间可能不是个好主意,对吧? - Girish
14
等待固定秒数是没有用的。那就是瞎猜。 - Anders Lindén
鉴于页面的JavaScript可以运行任何通用代码,编写一个程序等待其完成是不可能的。这是停机问题(https://en.wikipedia.org/wiki/Halting_problem)的一种形式。这里的任何解决方案都将需要做出妥协或基于底层网页的假设。 - speedplane
如下面的答案所述,大多数事情已经等待(driver.get())。一个不等待的特殊情况是表单提交点击按钮,请参见Python Selenium - Wait until next page has loaded after form submit - Stack Overflow - user202729
27个回答

1

我尝试了这段代码,它对我有效。每当我转到另一个页面时,我都会调用此函数。

public static void waitForPageToBeReady() 
{
    JavascriptExecutor js = (JavascriptExecutor)driver;

    //This loop will rotate for 100 times to check If page Is ready after every 1 second.
    //You can replace your if you wants to Increase or decrease wait time.
    for (int i=0; i<400; i++)
    { 
        try 
        {
            Thread.sleep(1000);
        }catch (InterruptedException e) {} 
        //To check page ready state.

        if (js.executeScript("return document.readyState").toString().equals("complete"))
        { 
            break; 
        }   
      }
 }

1

看看Tapestry Web框架。你可以在那里下载源代码

这个想法是通过body的html属性来表示页面已准备好。你可以使用这个想法来忽略复杂的用例。

<html>
<head>
</head>
<body data-page-initialized="false">
    <p>Write you page here</p>

    <script>
    $(document).ready(function () {
        $(document.body).attr('data-page-initialized', 'true');
    });
    </script>  
</body>
</html>

然后根据Tapestry框架创建Selenium WebDriver的扩展。

public static void WaitForPageToLoad(this IWebDriver driver, int timeout = 15000)
{
    //wait a bit for the page to start loading
    Thread.Sleep(100);

    //// In a limited number of cases, a "page" is an container error page or raw HTML content
    // that does not include the body element and data-page-initialized element. In those cases,
    // there will never be page initialization in the Tapestry sense and we return immediately.
    if (!driver.ElementIsDisplayed("/html/body[@data-page-initialized]"))
    {                
        return;
    }

    Stopwatch stopwatch = Stopwatch.StartNew();

    int sleepTime = 20;

    while(true)
    {
        if (driver.ElementIsDisplayed("/html/body[@data-page-initialized='true']"))
        {
            return;
        }

        if (stopwatch.ElapsedMilliseconds > 30000)
        {
            throw new Exception("Page did not finish initializing after 30 seconds.");
        }

        Thread.Sleep(sleepTime);
        sleepTime *= 2; // geometric row of sleep time
    }          
}

使用扩展程序ElementIsDisplayed 由Alister Scott编写
public static bool ElementIsDisplayed(this IWebDriver driver, string xpath)
{
    try
    {
        return driver.FindElement(By.XPath(xpath)).Displayed;
    }
    catch(NoSuchElementException)
    {
        return false;
    }
}

最后创建测试:
driver.Url = this.GetAbsoluteUrl("/Account/Login");            
driver.WaitForPageToLoad();

1

我在我的机器上无法编译Ben Dryer的答案("The method until(Predicate<WebDriver>) is ambiguous for the type WebDriverWait")。

适用于Java 8的工作版本:

Predicate<WebDriver> pageLoaded = wd -> ((JavascriptExecutor) wd).executeScript(
        "return document.readyState").equals("complete");
new FluentWait<WebDriver>(driver).until(pageLoaded);

Java 7版本:
Predicate<WebDriver> pageLoaded = new Predicate<WebDriver>() {

        @Override
        public boolean apply(WebDriver input) {
            return ((JavascriptExecutor) input).executeScript("return document.readyState").equals("complete");
        }

};
new FluentWait<WebDriver>(driver).until(pageLoaded);

1
等待document.ready事件并不能完全解决这个问题,因为这段代码仍处于竞争状态:有时候这段代码在点击事件被处理之前就被触发了,所以直接返回,因为浏览器还没有开始加载新页面。
经过一些搜索,我在Obay the testing goat上找到了一个解决方案。该解决方案的C#代码如下:
 IWebElement page = null;
 ...
 public void WaitForPageLoad()
 {
    if (page != null)
    {
       var waitForCurrentPageToStale = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
       waitForCurrentPageToStale.Until(ExpectedConditions.StalenessOf(page));
    }

    var waitForDocumentReady = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
    waitForDocumentReady.Until((wdriver) => (driver as IJavaScriptExecutor).ExecuteScript("return document.readyState").Equals("complete"));

    page = driver.FindElement(By.TagName("html"));

}

我会在driver.navigate.gotourl之后立即调用此方法,以便尽快获得页面的引用。玩得开心!


0

以下代码应该可以正常工作:

WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.presenceOfAllElementsLocated(By.xpath("//*")));

0

就像Rubanov为C#所写的一样,我为Java编写了它,它是:

    public void waitForPageLoaded() {
    ExpectedCondition<Boolean> expectation = new
            ExpectedCondition<Boolean>() {
                public Boolean apply(WebDriver driver) {
                    return (((JavascriptExecutor) driver).executeScript("return document.readyState").toString().equals("complete")&&((Boolean)((JavascriptExecutor)driver).executeScript("return jQuery.active == 0")));
                }
            };
    try {
        Thread.sleep(100);
        WebDriverWait waitForLoad = new WebDriverWait(driver, 30);
        waitForLoad.until(expectation);
    } catch (Throwable error) {
        Assert.fail("Timeout waiting for Page Load Request to complete.");
    }
}

0

我执行了一段JavaScript代码来检查文档是否已准备就绪。这为我节省了很多时间,调试那些具有客户端渲染的站点的Selenium测试。

public static boolean waitUntilDOMIsReady(WebDriver driver) {
def maxSeconds = DEFAULT_WAIT_SECONDS * 10
for (count in 1..maxSeconds) {
    Thread.sleep(100)
    def ready = isDOMReady(driver);
    if (ready) {
        break;
    }
}

}

public static boolean isDOMReady(WebDriver driver){
    return driver.executeScript("return document.readyState");
}

0
你可以编写一些逻辑来处理这个问题。我已经编写了一个方法,它将返回 WebElement,并且该方法将被调用三次,或者您可以增加时间并添加对 WebElement 的空值检查。以下是一个示例:
public static void main(String[] args) {
        WebDriver driver = new FirefoxDriver();
        driver.get("https://www.crowdanalytix.com/#home");
        WebElement webElement = getWebElement(driver, "homekkkkkkkkkkkk");
        int i = 1;
        while (webElement == null && i < 4) {
            webElement = getWebElement(driver, "homessssssssssss");
            System.out.println("calling");
            i++;
        }
        System.out.println(webElement.getTagName());
        System.out.println("End");
        driver.close();
    }

    public static WebElement getWebElement(WebDriver driver, String id) {
        WebElement myDynamicElement = null;
        try {
            myDynamicElement = (new WebDriverWait(driver, 10))
                    .until(ExpectedConditions.presenceOfElementLocated(By
                            .id(id)));
            return myDynamicElement;
        } catch (TimeoutException ex) {
            return null;
        }
    }

0
如果您的页面或网络连接速度较慢,那么上述所有方法都可能无效。我已经尝试过它们所有,唯一有效的方法是等待页面上最后一个可见元素加载完成。以Bing网页为例,他们在主搜索按钮旁边放置了一个相机图标(按图搜索按钮),只有在完整页面加载完成后才会显示。如果每个人都这样做,那么我们只需要像上面的示例一样使用显式等待即可。

0

在Java中,它会像下面这样:

  private static boolean isloadComplete(WebDriver driver)
    {
        return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("loaded")
                || ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
    }

1
最好将JS命令合并为一个,这样您就不必两次访问页面了,例如 return document.readyState == 'loaded' || return document.readyState == 'complete' - JeffC

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