使用Webdriver的Selenium超时处理的最佳方法是什么?

3
我遇到了一个问题。我的网页有一个DropDownList控件。一旦DropDownList的值改变(通过选择不同的值),页面就会刷新并呈现内容。
然后我必须在使用FindElement之前使用Thread.Sleep(2000)。
我的问题是:等待页面加载完成的最佳方法是什么?
我在代码中有很多Thread.Sleep(2000)实例,我开始认为这不是解决问题的最佳方法。
以下是我的代码:
[TestInitialize()]
public void Setup()
{
    if (BaseIntegrationTest.browserType.Equals(BaseIntegrationTest.IE))
    {
        driver = new InternetExplorerDriver();
    }
    else if (BaseIntegrationTest.browserType.Equals(BaseIntegrationTest.CHROME))
    {
        //driver = new ChromeDriver();
    }
    else if (BaseIntegrationTest.browserType.Equals(BaseIntegrationTest.FIREFOX))
    {
        driver = new FirefoxDriver();
    }
}

第二部分:
[TestMethod]
public void testVerifyData()
{
    // ...................
    // ...................
    driver.FindElement(By.XPath("//*[@id='ctl00_NavigationControl1_lnke']")).Click();

    Thread.Sleep(2000);

    //select from the dropdownlist.
    IWebElement catagory = driver.FindElement(By.Id("ctl00_ContentPlaceHolder1_Filter"));
    SelectElement selectCatagory = new SelectElement(catagory);
    selectCatagory.SelectByText("Employee");

    Thread.Sleep(2000);
    // ...................
    // ...................
}
6个回答

7

使用Thread.Sleep()来实现等待是一种不推荐的方式。

这段代码在Selenium文档中有详细描述 http://seleniumhq.org/docs/04_webdriver_advanced.html

WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
IWebElement category = wait.Until<IWebElement>((d) =>
    {
        return d.FindElement(By.Id("ctl00_ContentPlaceHolder1_Filter"));
    });

这是一个显式等待的例子,Selenium会在找到元素之前不执行任何操作。

隐式等待的例子如下:

driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(10));
IWebElement category = driver.FindElement(By.Id("ctl00_ContentPlaceHolder1_Filter"));

在隐式等待中,驱动程序将等待一定的时间,并轮询DOM以查找不存在的任何元素。

编辑

public WaitForElement(string el_id)
{
    WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
    IWebElement category = wait.Until<IWebElement>((d) =>
    {
        return d.FindElement(By.Id(el_id));
    });
}

谢谢Greg,那我需要为每个ID都做吗?还是有更抽象的方法可以实现? - Nick Kahn
如果你喜欢的话,可以接受这个答案XD。如果你想重复使用它,只需将其制作成一个对象,然后将id作为参数或其他内容提供给该对象,以便在需要时重复使用它。这就是我对所有等待操作所做的事情。 - Greg
只是好奇,我应该为所有 Id 包装 WaitForElement 还是只包装那些需要时间的 Id?或者什么是最佳实践... - Nick Kahn
只有你知道会花费时间的元素才需要等待。因此,至少通过设置某种等待时间,告诉机器执行额外的指令需要更多时间,从而减慢测试速度。如果使用FindElement可靠地找到元素,则不必使用等待。但是,如果该元素是在打开新页面或其他操作后要查找的第一个元素,则可能需要等待。 - Greg

1

有些情况下,确实需要等待,这时候使用Task.Delay方法比Thread.Sleep方法更可预测。

Task.Delay(1000).Wait(); // 等待1秒钟


1
我也使用了WebDriverWait来解决这个问题。 但是,我设置等待最后一个元素出现(例如页脚,列表中的最后一项)。
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(d => d.FindElement(By.Id("footer")).Displayed);

或者

WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(d => d.FindElements(By.TagName("li")).Last().Displayed);

0
我为自己制作了一个自定义函数,也许对某些人有用:
 public static void waitForPageToBeLoaded(WebDriver driver, int time) {

     ExpectedCondition<Boolean> expectation = new
     ExpectedCondition<Boolean>() {     
         public Boolean apply(WebDriver driver) {
             return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
            }
     };

     Wait<WebDriver> wait = new WebDriverWait(driver, Long.valueOf(time)); 
     try {
         wait.until(expectation);
     } catch(Throwable error) {
         System.out.println(error);
         mainFunctions.reportFailure("[waitForPageToBeLoaded] Page load took longer than " + time/60 + " min.");
     }
 }  // waitForPageLoaded

0

如果你想在页面加载完成或超时后获取HTML:

using (var chromeDriver = new ChromeDriver())
        {
            chromeDriver.Manage().Timeouts().SetPageLoadTimeout(TimeSpan.FromSeconds(30));

            try
            {
                chromeDriver.Navigate().GoToUrl(link);
                return chromeDriver.PageSource;
            }
            catch (WebDriverTimeoutException)
            {
                return chromeDriver.PageSource;
            }
        }

0

假设交互引起的刷新是Selenium可以检测到的,那么以下方法可能会满足您的需求:

waitForPageToLoad

据我所知,此方法检查浏览器内部状态以查看浏览器是否认为正在加载某些内容,并等待直到该状态被清除。然而,这个准备状态有点老旧 - 有很多动态加载页面或页面的一部分的方式,因此准备状态并不总是准确反映正在发生的事情。

如果您知道您的操作会导致什么变化 - 也就是说,如果您知道在与下拉列表交互后,将出现之前页面上没有的特定UI元素 - 那么最好等待这些特定的UI元素出现。您可以编写一个方法,重复检查isTextPresent并等待一小段时间,直到达到超时或出现预期的元素。在Selenium接口中,还有其他可用于这些方法的“is X Present”方法,您可以在其他地方找到。


waitForPageToLoad不是Selenium 2中的API。 - Greg
糟糕,我长时间不接触Selenium了,现在又要重新开始学习。有很多东西要学习。 :) 谢谢! - James Martineau

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