使用Selenium 2 WebDriver等待Ajax调用完成

59

我正在使用Selenium 2 WebDriver测试一个使用AJAX的UI。

有没有方法让驱动程序等待一会儿,直到Ajax请求完成。

基本上,我有这个:

d.FindElement(By.XPath("//div[8]/div[3]/div/button")).Click();
// This click trigger an ajax request which will fill the below ID with content.
// So I need to make it wait for a bit.

Assert.IsNotEmpty(d.FindElement(By.Id("Hobbies")).Text);

这是用什么语言编写的? - jcollum
1
@jcollum 这是 C#。 - Omu
10个回答

82

如果您使用jQuery进行ajax请求,可以等待jQuery.active属性为零。其他库可能具有类似选项。

public void WaitForAjax()
{
    while (true) // Handle timeout somewhere
    {
        var ajaxIsComplete = (bool)(driver as IJavaScriptExecutor).ExecuteScript("return jQuery.active == 0");
        if (ajaxIsComplete)
            break;
        Thread.Sleep(100);
    }
}

1
这是C#语言,但是这种模式也适用于其他实现方式。 - Morten Christiansen
还可以查看 https://watirmelon.blog/2015/05/14/waiting-for-ajax-calls-in-webdriver-c/ - Terence Golla
那么,你是告诉我每隔100毫秒就应该检查一次它是否被重置了吗?当jQuery端的ajax成功方法被调用时,我能否触发它?只是在寻找更好的解决方案。 - Jamshaid K.

46

您还可以在这里使用Selenium显式等待。这样,您就不需要自己处理超时。

public void WaitForAjax()
{
    var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(15));
    wait.Until(d => (bool)(d as IJavaScriptExecutor).ExecuteScript("return jQuery.active == 0"));
}

6
有没有一种不使用JQuery的方式来实现AJAX呢? - Merkidemis
当没有使用jQuery时,我无法找到一种通用的方法来解决这个问题。似乎需要与开发人员一起为每个单独的情况解决这个问题。 - Mila
请注意,WebDriverWait 需要 Selenium.Support NuGet 包。 - ReinierDG
这篇帖子救了我的整个一周。 - Xyloto
这在jQuery 1.12.4中对我不起作用...有人有任何想法为什么吗? - Kenny83

18
var wait = new WebDriverWait(d, TimeSpan.FromSeconds(5));
var element = wait.Until(driver => driver.FindElement(By.Id("Hobbies")));

1
你确定这是正确的方式吗?因为它并没有帮助到我。 - Omu
1
@hhastekin,Ajax调用是即时的,看起来根本不需要等待,因为可见元素是正常的,但对于style="display:none;"元素执行.Text会返回空。 - Omu
2
@Omu,WebDriver的设计使其仅显示真实用户可见的内容。如果用户看不到它,那么WebDriver也看不到它。 - Hakan Hastekin
2
@hhastekin,它可以看到input type:hidden的value属性。 - Omu
@Omu,可以查看一下这个链接:http://code.google.com/p/selenium/wiki/FrequentlyAskedQuestions,也许你能在那里找到答案。 - Hakan Hastekin
显示剩余2条评论

8

基于Morten Christiansens的答案的Java解决方案

    public void WaitForAjax() throws InterruptedException
    {
while (true) { //使用JavaScript执行器判断jQuery是否处于活动状态 Boolean ajaxIsComplete = (Boolean) ((JavascriptExecutor)driver).executeScript("return jQuery.active == 0"); if (ajaxIsComplete){ break; } Thread.sleep(100); } }

3

只需添加一个超时参数即可进行小幅改进:

internal static void WaitForAllAjaxCalls(this ISelenium selenium, IWebDriver driver, int timeout = 40)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        while (true)
        {
            if (sw.Elapsed.Seconds > timeout) throw new Exception("Timeout");
            var ajaxIsComplete = (bool)driver.ExecuteScript("return jQuery.active == 0");
            if (ajaxIsComplete)
                break;
            Thread.Sleep(100);
        }            
    }

2

只是小改进:


//Wait for Ajax call to complete
  public void WaitForAjax1() throws InterruptedException
    {

        while (true)
        {
            if ((Boolean) ((JavascriptExecutor)driver).executeScript("return jQuery.active == 0")){
                break;
            }
            Thread.sleep(100);
        }
    }

1

这是我的代码:

public static void WaitForCommission (WebDriver driver) throws Exception {
    for (int second = 0;; second++) {
        if (second >= 30) fail("timeout");
        try { 
            if (IsElementActive(By.id("transferPurposeDDL"), driver)) 
                break; 
            } catch (Exception e) {}
        Thread.sleep(1000);
    }
}

private static boolean IsElementActive(By id, WebDriver driver) {
    WebElement we =  driver.findElement(id);        
    if(we.isEnabled())
        return true;
    return false;
}

这段代码真的很有效。


1
如果您使用 Coypu,则可以在AJAX调用后检查元素是否存在,然后单击它:
private void WaitUntilExistsThenClick(string selectorId)
{
    var searchBoxExists = new State(() => browser.FindId(selectorId).Exists());
    if (browser.FindState(searchBoxExists) == searchBoxExists)
    {                
        browser.FindId(selectorId).Click();
    }
}       

1
如果您正在使用Graphene,您可以使用以下内容:
Graphene.waitModel().until((Predicate<WebDriver>) input -> (Boolean) ((JavascriptExecutor) input).executeScript("return jQuery.active == 0"));

1
“XML Http Request” 是用于向服务器发送 Ajax 请求的协议,因此这种请求的存在表示正在进行基于 Ajax 的操作。
有许多浏览器插件可以让您监视浏览器发送的 XML Http 请求。我个人使用 Firefox 的 Firebug 插件,它是一个非常有用的工具。安装 Firebug 后,它会在浏览器窗口右下角显示一个类似虫子的图标。单击虫子图标即可启动 Firebug,如上图所示。选择“Net”,然后选择“XHR”以启动 XHR 控制台,在那里将显示浏览器发送的所有 XML HTTP 请求。
尽可能避免使用 thread.sleep()。以下是一段代码,它接受等待时间作为输入并运行指定时间的秒表。
您可以首先将输入时间设置为 30 秒。
protected void WaitForAjaxToComplete(int timeoutSecs)
        {

            var stopWatch = new Stopwatch();

            try
            {
                while (stopWatch.Elapsed.TotalSeconds < timeoutSecs)
                {

                    var ajaxIsComplete = (bool)(WebDriver as IJavaScriptExecutor).ExecuteScript("return jQuery.active == 0");
                    if (ajaxIsComplete)
                    {
                        break;
                    }

                }
            }
            //Exception Handling
            catch (Exception ex)
            {
                stopWatch.Stop();
                throw ex;
            }
            stopWatch.Stop();

        }

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