Selenium – 等待网络流量

我们将Selenium与Java API和一些Javascript用户扩展一起使用。 我们在应用程序中使用了大量的AJAX调用。 我们的很多测试都是随机失败的,因为有时候AJAX调用比其他时候完成得慢,所以页面没有完全加载。 我们通过等待特定元素或Thread.sleep来解决这个问题。 我试图找到一种方法,而不是等待网络流量完成。 这样我们就可以这样做:

selenium.click("some JS button"); selenium.waitForNetwork(); assertTrue(something); 

这样我们可以摆脱线程hibernate,并且当服务器响应更快并且由于时序问题而没有那么多测试失败时,测试通过得更快。

我无法找到一种方法来搜索Google。 有没有人有任何想法我们如何才能做到这一点? (最好是通过Javascript或Java API,但欢迎所有建议)。

注意:“waitFor”的其他变体并不是我想要的。 我们已经在点击和其他内容中使用了这些内容。 我正在寻找等待NETWORK TRAFFIC的东西。 感谢所有的反馈,我会尝试一些建议,但我仍然对其他想法持开放态度。

谢谢。

Selenium中有几种类型的waitFor可用。 在您的测试用例中,如果您知道页面加载时会出现特定文本,则可以使用waitForText。 如果有不同的DOM元素被填充(通过Ajax / pageload),您可以顺序添加waitForText。

在尝试了此页面上描述的所有变体后,我们最终采用了此策略:

注入一个特殊的javascript,它基本上覆盖了所有相关的框架方法,并保留了未完成请求数的计数器 ,这些请求在启动时递增,在完成时递减。 由于javascript的异步性质,保留一个布尔变量是不够的,你很可能在你自己的逻辑中结束竞争条件。

如果您正在使用视觉效果,您可能也希望对视觉效果这样做; 变形/褪色和其他视觉效果会有同样的问题。

所以我们基本上做一个常规的“点击”,然后总是等待这个计数器再次达到0,使用javascript,就像其他人在这里建议的那样。 在我们实现这一目标之后,我们将瞬态问题减少到无。

请记住,在处理完所有同步onclick事件之前,selenium不会从“click”返回,因此您必须确保将这些计数器增加为onclick的直接后果。

有关如何覆盖框架方法的说明,您需要查阅框架文档; 我们在原型中使用addMethods。 可以将此覆盖保留在运行测试时仅添加到页面的特殊javascript中。

更新:我使用与Selenium2相同的方法(wrapRequest),它仍然有效。 唯一的区别是我不使用用户扩展。 我只是执行javascript来查询JSP加载的代码。

这就是我们最终做的事情。 (注意,如果你总是只使用像JQuery这样的单个框架,那么如果运行Ajax调用有更简单的方法。我们不得不更多地使用jimmy-rig,因为我们有几个页面使用JQuery和一堆使用GWT)

我创建了一个Javascript文件,只有在您测试时才会加载每个页面。 (我通过在JSP模板中包含一个片段来实现这一点:如果测试查询字符串param作为true传入,则设置一个cookie。如果设置了该cookie,则包含javascript文件)这个javascript文件实际上包装XMLHttpRequest对象以保持计数所有发送请求:

 function wrapRequest(xmlRequest) { var oldSend = xmlRequest.prototype.send; xmlRequest.prototype.send = function() { var oldOnReady = this.onreadystatechange; oldOnReady = function() { oldOnReady.apply(this, arguments); // if call is complete, decrement counter. } // increment counter if(oldSend.apply) oldSend.apply(this, arguments); else if (oldOnReady.handleEvent) { //Firefox sometimes will need this oldOnReady.handleEvent.apply(this, arguments); } } } 

如果async == false,那还有一点,但这应该很容易弄明白。

然后我向selenium user-extensions.js添加了一个函数,它看起来像这样:

 Selenium.prototype.getIsNetworkReady = function() { if(this.browserbot.topWindow.isNetworkReady) { //sometimes this method is called before the page has loaded the isNetworkReady function return this.browserbot.topWindow.isNetworkReady(); } return false; } 

另外,请注意topWindow上的检查:这是因为在选择iframe时会出现问题。

此外,您必须在每个加载的iFrame的XMLHttpRequest对象上调用wrapRequest方法。 我现在正在使用: document.addEventListener("DOMNodeInserted", onElementAdded, true); 然后在onElementAdded中我只是在加载iframe时抓取它并传入XMLHttpRequest对象。 但这在IE中不起作用,所以我正在寻找替代方案。 如果有人知道更好的方法,所以它适用于IE和FF我很乐意听到它。

无论如何,这是实施的要点。 希望这有助于未来的任何人。

我们已经有了客户端代码来在AJAX请求期间更改鼠标光标(使用a4j:status with onstartonstop )。 因此我们可以简单地等待JavaScript表达式的值

 window.document.body.style.cursor 

但是,我发现这不能可靠地工作,即有时候测试进行得太早。 我不确定原因,但怀疑在调用onstop时不一定完全更新DOM。

至于减少不必要的延迟,您可以自己实施轮询并比selenium更频繁地进行轮询。

这可能不适合你,但由于类似的问题我们转移到Selenium 2 (又名WebDriver)。 我们将PageFactory与AjaxElementLocatorFactory 一起使用

为了使迁移更容易,有一个Selenium仿真 ,计划是完全合并Selenium 1和WebDriver,这是Selenium 2的主要目标。

是的,做这样的事情(Pseudocode):

 int i = 0; while (element is not yet present) { i++; if (i>TRESHOLD) { throw an exception } Thread.sleep(200); } //go on 

你可能想把它塞进一个函数中。