Recently divorced so many different applications, AJAX-. In fact such a test automation application is different from the usual test automation WEB-application, but there are a few subtleties. One of the details - it’s just waiting for the completion of all the AJAX-request. For example, if a certain mark on the page is checkbox’a update any select’a of AJAX-request, then the test, which immediately after the mark selects a specific option, fall down, because option’a this will not be there. And all because the test itself is much faster than the AJAX-request to update the list.
In this case, the Automator has multiple outputs.
Put mark after sleep checkbox’a.
This is the worst, and unfortunately, most often applied solution.
We do not know in advance how long it will take to complete AJAX-request, respectively, have set the waiting time based on the minimally sufficient for most cases. For example, 5 seconds. When these expectations by 5 seconds typed enough, our tests will take a long time, even when all the AJAX-requests are fast.
In addition, sometimes for different reasons runtime AJAX-query may be 5.2 seconds, in which case we will get the false drop test, which is also bad.
Wait and take advantage of the class to wait for Selenium # isElementPresent not return true for the desired option’a.
This method is better, but still should not be applied in the future I will write in detail why. Better instead to use the method of the class Wait Selenium # waitForCondition, and where to wait for the desired item.
Somehow, after a mark checkbox’a wait until all the AJAX-request, and only then choose the option.
This method of study in more detail, because he is quite versatile and simple in terms of Automator.
In most of WEB-applications to work with AJAX, specialized libraries (jQuery, Prototype, Dojo, etc.) that provide developers a higher level of abstraction than the standard API, and respectively, and greater flexibility.
In order to test in Selenium-wait for the completion of all the AJAX-request, you must learn how to monitor these requests globally. The standard API does not have the possibility of installing global hooks, but practically in every third-party libraries such a possibility, although it is done everywhere in his own way. Here is an example of how to wait for the completion of all AJAX-requests when using the library jQuery:
/ **
* Waits for all active jQuery AJAX requests to finish.
*
* @ Param timeout Timeout in milliseconds.
* @ Throws SeleniumError If timeout is reached.
* /
Selenium.prototype.doWaitForJqueryAjaxRequests = function (timeout) {
return Selenium.decorateFunctionWithTimeout (function () {
return selenium.browserbot.getUserWindow (). jQuery.active == 0;
}, Timeout);
};
Here, we simply wrap a necessary condition for us (the number of active AJAX-request is equal to zero) in the method of Selenium # decorateFunctionWithTimeout, which would be expected to fulfill this condition within the specified timeout in time, and if you wait, the method will be successfully completed, otherwise it will be ejected exception SeleniumError .
If we describe the meta-language that we need to create a universal method of waiting, you get something like this:
Determine which libraries are used to working with AJAX.
Wait the completion of all AJAX-requests for each of the libraries used.
Quite simply, it remains to this in JavaScript, and plug in an extension to Selenium RC or to Selenium IDE, as you like. When using Selenium RC for greater versatility can load extension code using the DefaultSelenium # setExtensionJs.
That realization is ready (supports jQuery, Prototype and Dojo):
/ **
* Waits for all active AJAX requests to finish during specified timeout. Works only for AJAX requests which are
* Instantiated using one of the following frameworks: jQuery, Prototype, Dojo. Don’t work (immediately returns without
* Any errors) if standard AJAX API or one of other frameworks is used to send XML HTTP request.
*
* @ Param timeout Timeout in milliseconds.
* @ Throws SeleniumError If timeout is reached.
* /
Selenium.prototype.doWaitForAjaxRequests = function (timeout) {
return Selenium.decorateFunctionWithTimeout (function () {
var userWindow = selenium.browserbot.getUserWindow ();
var isJqueryComplete = typeof (userWindow.jQuery)! = ‘function’ | | userWindow.jQuery.active == 0;
var isPrototypeComplete = typeof (userWindow.Ajax)! = ‘function’ | | userWindow.Ajax.activeRequestCount == 0;
var isDojoComplete = typeof (userWindow.dojo)! = ‘function’ | | userWindow.dojo.io.XMLHTTPTransport.inFlight.length == 0;
return isJqueryComplete & & isPrototypeComplete & & isDojoComplete;
}, Timeout);
};
If writing tests is not used Selenese, a normal programming language, in order to be able to use the new method should be used to expand the driver to take this method like so:
import com.thoughtworks.selenium.CommandProcessor;
import com.thoughtworks.selenium.DefaultSelenium;public class CustomSelenium extends DefaultSelenium {
public CustomSelenium (String serverHost, int serverPort, String browserStartCommand, String browserURL) {
super (serverHost, serverPort, browserStartCommand, browserURL);
}public CustomSelenium (CommandProcessor processor) {
super (processor);
}/ **
* Waits for all active AJAX requests to finish during specified timeout. Works only for AJAX requests which are
* Instantiated using one of the following frameworks: jQuery, Prototype, Dojo. Don’t work (immediately returns
* Without any errors) if standard AJAX API is used to send request.
*
* @ Param timeout Timeout in milliseconds.
* /
public void waitForAjaxRequests (final int timeout) {
commandProcessor.doCommand ("waitForAjaxRequests", new String [] {String.valueOf (timeout)});
}
}
Now we can easily replace a test code here:
…
selenium.check ("name = enableBender");
sleep (5000);
selenium.select ("name = mode", "label = Kill all humans");
…To this:
…
selenium.check ("name = enableBender");
selenium.waitForAjaxRequests (60000);
selenium.select ("name = mode", "label = Kill all humans");
…
And the tests will be performed at a rate equal to the rate of response from the server, ie, without unnecessary delay.
For some projects, where the AJAX-request shall commence immediately after loading the page (yes, there are such), I recommend to overload methods waitForPageToLoad, waitForFrameToLoad and waitForPopUp, adding them to the last call waitForAjaxRequests, so as not to pull it all the time in tests.
Finally once again, that in the standard API is not possible to set global interceptors AJAX-requests, so this method will not work if developers use a standard API directly. Fortunately, in more or less serious projects do not do that. But it is quite possible that in some project uses its own wrapper around the standard API, in that case you will simply support the wrapper in the user-extensions.js.