如何让selenium等待Ajax响应?
我如何让selenium等待像日历小部件加载? 现在我只是在将testing用例导出到junit程序之后执行Thread.sleep(2500)
。
我会用
waitForElementPresent(locator)
这将等到元素出现在DOM中。
如果你需要检查元素是否可见,你可能会更好使用
waitForElementHeight(locator)
比等待一个元素更通用的解决scheme是等待到服务器的所有连接closures。 这将允许您等待所有ajax调用完成,即使它们没有任何callback,也不会影响页面。 更多细节可以在这里find。
使用C#和jQuery,我创build了以下方法来等待所有的AJAX调用完成(如果有人有更直接的方式从C#访问JSvariables,请评论):
internal void WaitForAjax(int timeOut = 15) { var value = ""; RepeatUntil( () => value = GetJavascriptValue("jQuery.active"), () => value == "0", "Ajax calls did not complete before timeout" ); } internal void RepeatUntil(Action repeat, Func<bool> until, string errorMessage, int timeout = 15) { var end = DateTime.Now + TimeSpan.FromSeconds(timeout); var complete = false; while (DateTime.Now < end) { repeat(); try { if (until()) { complete = true; break; } } catch (Exception) { } Thread.Sleep(500); } if (!complete) throw new TimeoutException(errorMessage); } internal string GetJavascriptValue(string variableName) { var id = Guid.NewGuid().ToString(); _selenium.RunScript(String.Format(@"window.$('body').append(""<input type='text' value='""+{0}+""' id='{1}'/>"");", variableName, id)); return _selenium.GetValue(id); }
如果使用python,你可以使用这个函数,点击button并等待DOM更改:
def click_n_wait(driver, button, timeout=5): source = driver.page_source button.click() def compare_source(driver): try: return source != driver.page_source except WebDriverException: pass WebDriverWait(driver, timeout).until(compare_source)
(CREDIT:基于这个堆栈溢出的答案 )
使用webdriver又名selenium2,您可以使用http://docs.seleniumhq.org/docs/04_webdriver_advanced.jsp#implicit-waits上提到的隐式等待configuration
使用Java:
WebDriver driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); driver.get("http://somedomain/url_that_delays_loading"); WebElement myDynamicElement = driver.findElement(By.id("myDynamicElement"));
或者使用python:
from selenium import webdriver ff = webdriver.Firefox() ff.implicitly_wait(10) # seconds ff.get("http://somedomain/url_that_delays_loading") myDynamicElement = ff.find_element_by_id("myDynamicElement")
我有一个类似的情况,我想等待的Ajax请求,使加载面板将消失,我已经检查了请求前后的HTML,发现有一个div为ajax加载面板,dix显示在ajax请求,并在请求结束后隐藏。 我创build了一个等待面板显示的function,然后等待它被隐藏
public void WaitForModalPanel() { string element_xpath = ".//*[@id='ajaxLoadingModalPanelContainer' and not(contains(@style,'display: none'))]"; WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0, 2, 0)); wait.Until(ExpectedConditions.ElementIsVisible(By.XPath(element_xpath))); element_xpath = ".//*[@id='ajaxLoadingModalPanelContainer' and contains(@style,'DISPLAY: none')]"; wait.Until(ExpectedConditions.ElementExists(By.XPath(element_xpath))); }
检查这个更多细节
下面的代码(C#)确保显示目标元素:
internal static bool ElementIsDisplayed() { IWebDriver driver = new ChromeDriver(); driver.Url = "http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp"; WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10)); By locator = By.CssSelector("input[value='csharp']:first-child"); IWebElement myDynamicElement = wait.Until<IWebElement>((d) => { return d.FindElement(locator); }); return myDynamicElement.Displayed; }
如果页面支持jQuery,则可以使用jQuery.active函数确保在所有ajax调用完成后检索目标元素:
public static bool ElementIsDisplayed() { IWebDriver driver = new ChromeDriver(); driver.Url = "http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp"; WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10)); By locator = By.CssSelector("input[value='csharp']:first-child"); return wait.Until(d => ElementIsDisplayed(d, locator)); } public static bool ElementIsDisplayed(IWebDriver driver, By by) { try { if (driver.FindElement(by).Displayed) { //jQuery is supported. if ((bool)((IJavaScriptExecutor)driver).ExecuteScript("return window.$ != undefined")) { return (bool)((IJavaScriptExecutor)driver).ExecuteScript("return $.active == 0"); } else { return true; } } else { return false; } } catch (Exception) { return false; } }
我写下一个方法作为我的解决scheme(我没有任何负载指标):
public static void waitForAjax(WebDriver driver, String action) { driver.manage().timeouts().setScriptTimeout(5, TimeUnit.SECONDS); ((JavascriptExecutor) driver).executeAsyncScript( "var callback = arguments[arguments.length - 1];" + "var xhr = new XMLHttpRequest();" + "xhr.open('POST', '/" + action + "', true);" + "xhr.onreadystatechange = function() {" + " if (xhr.readyState == 4) {" + " callback(xhr.responseText);" + " }" + "};" + "xhr.send();"); }
然后我只是用实际的驱动程序调用这个方法。 更多描述在这篇文章中 。
如上所述,您可以等待活动连接closures:
private static void WaitForReady() { WebDriverWait wait = new WebDriverWait(webDriver, waitForElement); wait.Until(driver => (bool)((IJavaScriptExecutor)driver).ExecuteScript("return jQuery.active == 0")); }
我的观察是,这是不可靠的,因为数据传输发生得非常快。 页面上的数据处理和呈现耗费了更多的时间,甚至jQuery.active == 0
数据可能还没有在页面上。
更明智的是使用显式等待元素显示在页面上,看到一些相关的答案。
最好的情况是,如果您的Web应用程序有一些自定义加载器或指示数据正在处理。 在这种情况下,您可以等待这个指示隐藏。
这是一个基于Morten Christiansen的答案的常规版本。
void waitForAjaxCallsToComplete() { repeatUntil( { return getJavaScriptFunction(driver, "return (window.jQuery || {active : false}).active") }, "Ajax calls did not complete before timeout." ) } static void repeatUntil(Closure runUntilTrue, String errorMessage, int pollFrequencyMS = 250, int timeOutSeconds = 10) { def today = new Date() def end = today.time + timeOutSeconds def complete = false; while (today.time < end) { if (runUntilTrue()) { complete = true; break; } sleep(pollFrequencyMS); } if (!complete) throw new TimeoutException(errorMessage); } static String getJavaScriptFunction(WebDriver driver, String jsFunction) { def jsDriver = driver as JavascriptExecutor jsDriver.executeScript(jsFunction) }
如果您正在等待的控件是“Ajax”Web元素,则下面的代码将等待它,或者其他任何Ajax Web元素完成加载或执行所需的任何操作,以便您可以更安全地继续执行步骤。
public static void waitForAjaxToFinish() { WebDriverWait wait = new WebDriverWait(driver, 10); wait.until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver wdriver) { return ((JavascriptExecutor) driver).executeScript( "return jQuery.active == 0").equals(true); } }); }
这对我来说就像是一种魅力:
public void waitForAjax(){
try { WebDriverWait driverWait = new WebDriverWait(driver, 10); ExpectedCondition<Boolean> expectation; expectation = new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver driverjs) { JavascriptExecutor js = (JavascriptExecutor) driverjs; return js.executeScript("return((window.jQuery != null) && (jQuery.active === 0))").equals("true"); } }; driverWait.until(expectation); } catch (TimeoutException exTimeout) { // fail code } catch (WebDriverException exWebDriverException) { // fail code } return this; }
这为我工作
public void waitForAjax(WebDriver driver) { new WebDriverWait(driver, 180).until(new ExpectedCondition<Boolean>(){ public Boolean apply(WebDriver driver) { JavascriptExecutor js = (JavascriptExecutor) driver; return (Boolean) js.executeScript("return jQuery.active == 0"); } }); }
对于那些正在使用primefaces的人,只需要:
selenium.waitForCondition("selenium.browserbot.getCurrentWindow().$.active==0", defaultWaitingPeriod);