2010-11-05 16 views
14

Wir verwenden Selenium mit der Java-API und einigen Javascript-Benutzererweiterungen. Wir verwenden viele AJAX-Anrufe in unserer App. Viele unserer Tests scheitern nach dem Zufallsprinzip, da manchmal die AJAX-Aufrufe langsamer enden als zu anderen Zeiten, so dass die Seite nicht vollständig geladen ist. Wir reparieren das, indem wir auf bestimmte Elemente oder Thread.sleep warten. Ich habe versucht, einen Weg zu finden, stattdessen nur auf den Abschluss des Netzwerkverkehrs zu warten. Damit wir dies tun könnte:Selenium - Warten auf Netzwerkverkehr

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

Auf diese Weise können wir loswerden der Faden schlafen und haben Tests schneller passieren, wenn der Server schneller reagiert und haben nicht so viele Tests wegen Zeitproblemen fehlschlagen.

Ich habe keine Möglichkeit gefunden, diese Suche nach Google zu tun. Hat jemand Ideen, wie wir das erreichen können? (Vorzugsweise entweder durch Javascript oder die Java API, aber alle Vorschläge sind willkommen).

Hinweis: die anderen Variationen von "waitFor" sind nicht das, wonach ich suche. Wir verwenden diese bereits in Klicks und anderen Dingen. Ich suche nach etwas, das auf den NETZWERKVERKEHR wartet. Danke für all das Feedback, ich werde ein paar Vorschläge ausprobieren, aber ich bin immer offen für andere Ideen.

Danke.

Antwort

2

Update: Ich die gleiche Methode verwenden (wrapRequest) in der gleichen Weise mit Selenium2 und es funktioniert immer noch. Der einzige Unterschied ist, dass ich keine Benutzererweiterung verwende. Ich führe einfach Javascript aus, um den von der JSP geladenen Code abzufragen.

Hier ist, was wir getan haben. (Beachten Sie, es gibt einfachere Möglichkeiten, wenn ein Ajax-Aufruf ausgeführt wird, wenn Sie immer nur ein einziges Framework wie JQuery verwenden. Wir mussten das ein wenig mehr jammy-rig, weil wir ein paar Seiten haben, die JQuery und eine Menge verwenden die GWT verwenden)

Ich erstellte eine Javascript-Datei, die jede Seite nur lädt, wenn Sie testen. (Ich tat dies, indem ich ein Snippet in unsere JSP-Vorlage einfügte: Wenn der Test-Querystring-Parameter als wahr übergeben wird, setze einen Cookie. Wenn dieser Cookie gesetzt ist, schließe die Javascript-Datei ein) Diese Javascript-Datei umschließt im Wesentlichen das zu haltende XMLHttpRequest-Objekt zählen aller Sendeaufträge:

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); 
     } 
    } 
} 

es es ein wenig mehr ist als das, wenn async == false, aber das sollte leicht genug sein, um herauszufinden.

Dann habe ich eine Funktion zu dem Selen Benutzer-extensions.js, die einfach wie folgt aussieht:

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; 
} 

Auch die Prüfung auf topWindow bemerken: Das ist, weil Sie Probleme bekommen, wenn ein iframe ausgewählt ist.

Außerdem müssen Sie die wrapRequest-Methode für das XMLHttpRequest-Objekt jedes iFrame aufrufen, der geladen wird. Ich mache das jetzt mit: document.addEventListener("DOMNodeInserted", onElementAdded, true); und dann in onElementAdded Ich greife nur den iframe, wenn es geladen ist und übergeben Sie das XMLHttpRequest -Objekt. aber das funktioniert nicht in IE, also suche ich nach einer Alternative. Wenn jemand einen besseren Weg kennt, so funktioniert es sowohl in IE als auch in FF. Ich würde es gerne hören.

Wie auch immer, das ist der Kern der Implementierung. Hoffe das hilft jedem in der Zukunft.

0

Ja, so etwas wie dieses (Pseudocode):

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

Sie wollen propably es in eine Funktion stopfen.

4

Es gibt einige Arten von WaitFor in Selen. Wenn Sie in Ihrem Testfall wissen, dass ein bestimmter Text beim Laden der Seite angezeigt wird, können Sie waitForText verwenden. Wenn verschiedene DOM-Elemente (über Ajax/Pageload) gefüllt werden, können Sie sequentiell waitForText hinzufügen.

1

Wir haben bereits clientseitigen Code, um den Mauszeiger während einer AJAX-Anfrage (mit a4j:status mit Attributen onstart und onstop) zu ändern. Deshalb können wir einfach waitFor den Wert des JavaScript-Ausdrucks

window.document.body.style.cursor 

Allerdings habe ich festgestellt, dass dies nicht zuverlässig funktioniert, das heißt manchmal der Test zu früh geht. Ich bin mir nicht sicher über die Ursache, aber vermute, dass das Aktualisieren des DOM nicht unbedingt vollständig durchgeführt wird, wenn onstop aufgerufen wird.

Um unnötige Verzögerungen zu vermeiden, können Sie die Abfrage selbst durchführen und häufiger abfragen als Selen.

4

Nachdem alle Variationen auf dieser Seite beschriebenen Versuch, am Ende haben wir mit dieser Strategie auf:

ein spezielles Javascript alle relevanten Rahmen Methoden und halten überschreibt einen Zähler injizieren, die im Grunde der Anzahl unerledigter Anfragen, welches beim Start inkrementiert und bei Beendigung dekrementiert wird. Aufgrund der asynchronen Art von Javascript ist es nicht ausreichend, um eine boolesche Variable zu halten, Sie werden wahrscheinlich mit Race-Bedingungen innerhalb Ihrer eigenen Logik enden.

Wenn Sie visuelle Effekte verwenden, möchten Sie dies wahrscheinlich auch für visuelle Effekte tun; Morphing/Fading und andere Visualisierungen haben das gleiche Problem.

Also machen wir grundsätzlich einen "Klick" und warten dann immer wieder darauf, dass dieser Zähler wieder 0 erreicht, mit Javascript, wie es die anderen hier vorschlagen. Nachdem wir dies erreicht hatten, reduzierten wir die vorübergehenden Probleme auf null.

Denken Sie daran, dass Selen nicht von "click" zurückkehrt, bis alle synchronen Onclick-Ereignisse verarbeitet sind. Sie müssen also sicherstellen, dass diese Zähler als direkte Folge von onclick inkrementiert werden.

Anweisungen zum Überschreiben von Framework-Methoden finden Sie in der Framework-Dokumentation. Wir verwenden addMethods im Prototyp. Es ist möglich, dies in einem speziellen Javascript zu behalten, das nur auf der Seite hinzugefügt wird, wenn Tests ausgeführt werden.

+0

Wir machen dasselbe für unser internes AJAX-Framework und rufen die Funktion IsTheCountZeroYet() in WaitForCondition() auf. Ich verstehe, dass Sie es einfach mit jQuery machen können. Andere Frameworks, vielleicht auch. –

+0

Das Interessante ist, dass die Ajax-Zähler in prototype.js fehlerhaft sind und nicht immer auf 0 zurückgehen.Und die visuellen Effekte haben diese Art von Zähler noch nicht. Also mussten wir unsere eigenen rollen. – krosenvold

1

Dies ist möglicherweise nicht akzeptabel für Sie, aber wegen der ähnlichen Probleme sind wir zu Selenium 2 (alias WebDriver) verschoben. Wir verwenden PageFactory mit AjaxElementLocatorFactory

Um eine Migration zu erleichtern, gibt es eine ist Selenium Emulation und der Plan ist vollständig Selen 1 und WebDriver zu fusionieren, die das Hauptziel von Selen ist 2.

Verwandte Themen