如何在Web驱动程序中检查页面是否完全加载?

33

我正在编写一些Java Webdriver代码来自动化我的应用程序。如何正确检查页面是否已加载?该应用程序还具有一些Ajax调用。

我已经为WebDriver声明了一个隐式等待。

10个回答

48
Selenium会为您处理这个问题,或者至少尽力而为。有时候它会无法胜任,您需要在一定程度上帮助它。通常的解决方案是隐式等待,可以解决大部分问题。
如果您真的知道自己在做什么以及为什么这样做,您可以尝试编写一个通用方法来检查页面是否完全加载。然而,这无法适应每个网站和每种情况。
相关问题:Selenium WebDriver: 等待包含JavaScript(JS)的复杂页面加载,请参见我在那里的回答。
简短版:你永远不会确定。
“正常”的加载很容易-document.readyState。当然,Selenium已经实现了这个。问题在于异步请求、 AJAX,因为您永远无法确定它是否完成。今天的大多数网页都有永久运行并且一直轮询服务器的脚本。
您可以在上面的链接中找到各种可以执行的操作。或者,像其他95%的人一样,隐式使用隐式等待和在必要时使用显式等待+预期条件
例如,单击后页面上的某个元素应该变为可见状态,您需要等待它:
WebDriverWait wait = new WebDriverWait(driver, 10);  // you can reuse this one

WebElement elem = driver.findElement(By.id("myInvisibleElement"));
elem.click();
wait.until(ExpectedConditions.visibilityOf(elem));

1
@Raghubansh,我不知道你刚才说了什么 : )。但是,我稍微编辑了一下答案,以使其更清晰。如果您有具体的问题需要解决,请发布一个相关问题,附上您的尝试,并我们会看看能为您做些什么 :)。 - Petr Janeček
1
谢谢!只有一个注意事项,您的样本代码在某个地方写成了elen而不是elem - Dawn Drescher
1
虽然这是一个老问题,但我应该指出,我认为默认情况下在将焦点切换到iframe时不会检查document.readyState - LetsPlayYahtzee
我想等待页面完全加载并找到特定的iframe。在这种情况下,预期条件是什么? - user2335580
我能否搜索特定的iframe是否已加载?假设我的元素ABC在iframe XYZ中,当我的页面加载时,如何检查我的iframe XYZ是否已加载?提供一些示例代码将会很有帮助。谢谢。 - user2335580
显示剩余5条评论

6

这是一个简单的代码段,可以直接使用,对我来说完美地工作。

static void waitForPageLoad(WebDriver wdriver) {
    WebDriverWait wait = new WebDriverWait(wdriver, 60);

    Predicate<WebDriver> pageLoaded = new Predicate<WebDriver>() {

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

    };
    wait.until(pageLoaded);
}

大多数情况下,这里等待一个已知元素。但有时您希望保护您不知道的页面上的行为。在我的情况下,JavaScript 缓慢加载会引起问题。因此,当您访问未知页面时,这是我最好的选择。该方法确实很强大。因此,我只是在本地等待检查 document.readyState。 - LeoPucciBr
你有什么观察,Leo? - Narendra Chandratre
有些页面在加载元素后(ajax),会添加链接到这些元素上。因此,当页面开始加载时,元素出现但没有正确的链接,驱动程序将会错误地进行操作。所以对我来说,最好的选择是在点击某个链接之前等待文档准备就绪状态。public void EsperaPagina(IWebDriver driver) { WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(20.0)); wait.Until<Boolean>(delegate (IWebDriver d){return (Boolean)((IJavaScriptExecutor)d).ExecuteScript("return document.readyState").Equals("complete"); });} - LeoPucciBr
1
这个答案只是复制了Selenium的内部功能。 - Corey Goldberg

4

我知道这篇文章有点老了。但在从以上所有代码中收集了所有内容后,我做出了一个不错的方法(解决方案),用于处理ajax运行和常规页面。该代码仅适用于C#语言(因为经过一年的折腾后,Selenium绝对是最佳选择之一)。

该方法被用作扩展方法,也就是说,简单来说,您可以在对象IWebDriver中添加更多功能(方法)。重要的是,您必须在参数中定义“this”以利用它。

超时变量是webdriver等待页面响应的秒数。使用'Selenium'和'Selenium.Support.UI'命名空间,可以执行一段返回布尔值的javascript代码,用于检查文档是否已准备好(complete)并且jQuery是否已加载。如果页面没有jQuery,则该方法将引发异常。此异常由错误处理“捕获”。在catch状态下,将仅检查文档的就绪状态,而不检查jQuery。

public static void WaitUntilDocumentIsReady(this IWebDriver driver, int timeoutInSeconds) {
    var javaScriptExecutor = driver as IJavaScriptExecutor;
    var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds));

    try {
        Func<IWebDriver, bool> readyCondition = webDriver => (bool)javaScriptExecutor.ExecuteScript("return (document.readyState == 'complete' && jQuery.active == 0)");
        wait.Until(readyCondition);
    } catch(InvalidOperationException) {
        wait.Until(wd => javaScriptExecutor.ExecuteScript("return document.readyState").ToString() == "complete");
    }
}

感谢您在这里发布,即使这是一个旧的帖子。我在EC2上启动Selenium测试时遇到了一些问题,而jquery部分解决了我的问题。谢谢! - J-Roel

3
您可以在Web页面中设置一个JavaScript变量,一旦加载完成就会被设置。您可以将其放置在任何位置,但如果您使用jQuery,则$(document).onReady是一个不错的起点。如果没有使用jQuery,则可以将其放在页面底部的<script>标签中。
与检查元素可见性相比,此方法的优点在于,您知道在wait语句执行后页面的确切状态。

MyWebPage.html

... All my page content ...
<script> window.TestReady = true; </script></body></html>

在您的测试中(C#示例):
// The timespan determines how long to wait for any 'condition' to return a value
// If it is exceeded an exception is thrown.
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5.0));

// Set the 'condition' as an anonymous function returning a boolean
wait.Until<Boolean>(delegate(IWebDriver d)
{
    // Check if our global variable is initialized by running a little JS
    return (Boolean)((IJavaScriptExecutor)d).ExecuteScript("return typeof(window.TestReady) !== 'undefined' && window.TestReady === true");
});

1
那仍然会存在竞态条件。您正在页面A上(已设置令牌变量),单击链接到页面B,然后立即告诉它等待变量被设置(假定在页面B上),但如果请求需要几秒钟才能开始,它将找到现有的页面A变量并愉快地继续进行。 - Edson Medina
当时我正在处理单页应用程序,因此那个问题从未出现过。 - jmathew

1

最近,当我处理一个AJAX应用程序/RIA时,我遇到了同样的问题! 我使用了隐式等待,时间大约为90秒。它会等待,直到元素可用...因此,我们可以做些什么来确保页面完全加载呢?

添加一个布尔语句,检查特定元素的条件是否存在并将其分配给一个变量,检查条件是否为真,只有在条件为真时,“执行必要的操作!” ...通过这种方式,我找到了两个等待都可以使用的方法...

例如:

@Before

{ implicit wait statement}

@Test

{

boolean tr1=Driver.findElement(By.xpath("xx")).isEnabled/isDisplayed;

if (tr1==true && ____)//as many conditions, to make sure, the page is loaded

{

//do the necessary set of actions...
driver.findElement(By.xpath("yy")).click();

}

}

希望这可以帮助到你!对我来说,它也处于实施阶段...


0

以下是我BasePageObject类中关于等待的一些代码:

public void waitForPageLoadAndTitleContains(int timeout, String pageTitle) {
    WebDriverWait wait = new WebDriverWait(driver, timeout, 1000);
    wait.until(ExpectedConditions.titleContains(pageTitle));
}

public void waitForElementPresence(By locator, int seconds) {
    WebDriverWait wait = new WebDriverWait(driver, seconds);
    wait.until(ExpectedConditions.presenceOfElementLocated(locator));
}

public void jsWaitForPageToLoad(int timeOutInSeconds) {
    JavascriptExecutor js = (JavascriptExecutor) driver;
    String jsCommand = "return document.readyState";

    // Validate readyState before doing any waits
    if (js.executeScript(jsCommand).toString().equals("complete")) {
        return;
    }

    for (int i = 0; i < timeOutInSeconds; i++) {
        TimeManager.waitInSeconds(3);
        if (js.executeScript(jsCommand).toString().equals("complete")) {
            break;
        }
    }
}

   /**
     * Looks for a visible OR invisible element via the provided locator for up
     * to maxWaitTime. Returns as soon as the element is found.
     *
     * @param byLocator
     * @param maxWaitTime - In seconds
     * @return
     *
     */
    public WebElement findElementThatIsPresent(final By byLocator, int maxWaitTime) {
        if (driver == null) {
            nullDriverNullPointerExeption();
        }
        FluentWait<WebDriver> wait = new FluentWait<>(driver).withTimeout(maxWaitTime, java.util.concurrent.TimeUnit.SECONDS)
                .pollingEvery(200, java.util.concurrent.TimeUnit.MILLISECONDS);

        try {
            return wait.until((WebDriver webDriver) -> {
                List<WebElement> elems = driver.findElements(byLocator);
                if (elems.size() > 0) {
                    return elems.get(0);
                } else {
                    return null;
                }
            });
        } catch (Exception e) {
            return null;
        }
    }

支持的方法:

     /**
     * Gets locator.
     *
     * @param fieldName
     * @return
     */
    public By getBy(String fieldName) {
        try {
            return new Annotations(this.getClass().getDeclaredField(fieldName)).buildBy();
        } catch (NoSuchFieldException e) {
            return null;
        }
    }

0
你可以使用driver.getPageSource()获取网站的HTML。如果在一定时间间隔内HTML没有改变,这意味着页面已经加载完成。一到两秒钟应该足够了。如果你想加快速度,你可以比较两个HTML的长度。如果它们的长度相等,那么HTML应该是相等的,这意味着页面已经完全加载。JavaScript解决方案对我没有起作用。

0
解决方案是使用隐式等待,这将解决页面加载的问题。
WebDriverWait waitForElement = new WebDriverWait(driver, 15);
WebElement element = driver.findElement(By.id("myElement"));
element.click(); 
wait.until(ExpectedConditions.visibilityOf(element));

0
这是我如何修复它的方式,使用代码片段给你一个基本的想法:
public class IFrame1 extends LoadableComponent<IFrame1> {

    private RemoteWebDriver driver;

    @FindBy(id = "iFrame1TextFieldTestInputControlID" ) public WebElement iFrame1TextFieldInput;
    @FindBy(id = "iFrame1TextFieldTestProcessButtonID" ) public WebElement copyButton;

    public IFrame1( RemoteWebDriver drv ) {
        super();
        this.driver = drv;
        this.driver.switchTo().defaultContent();
        waitTimer(1, 1000);
        this.driver.switchTo().frame("BodyFrame1");
        LOGGER.info("IFrame1 constructor...");
    }

    @Override
    protected void isLoaded() throws Error {        
        LOGGER.info("IFrame1.isLoaded()...");
        PageFactory.initElements( driver, this );
        try {
            assertTrue( "Page visible title is not yet available.", 
                    driver.findElementByCssSelector("body form#webDriverUnitiFrame1TestFormID h1")
                    .getText().equals("iFrame1 Test") );
        } catch ( NoSuchElementException e) {
            LOGGER.info("No such element." );
            assertTrue("No such element.", false);
        }
    }

    /**
     * Method: load
     * Overidden method from the LoadableComponent class.
     * @return  void
     * @throws  null
     */
    @Override
    protected void load() {
        LOGGER.info("IFrame1.load()...");
        Wait<WebDriver> wait = new FluentWait<WebDriver>( driver )
                .withTimeout(30, TimeUnit.SECONDS)
                .pollingEvery(5, TimeUnit.SECONDS)
                .ignoring( NoSuchElementException.class ) 
                .ignoring( StaleElementReferenceException.class ) ;
        wait.until( ExpectedConditions.presenceOfElementLocated( 
                By.cssSelector("body form#webDriverUnitiFrame1TestFormID h1") ) );
    }
....

-4

你可以截屏并将渲染后的页面保存在一个位置,然后检查截屏,以确定页面是否完全加载且没有损坏的图像。


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