Testing Ajax with WebDriver – Make your tests deterministic

We had some non-deterministic tests on our project so we decided to implement a generic way to test ajax calls using webdriver. I was quite impressed with how quickly we achieved this, mainly due to the fact that javascript is dynamic and functional. And also thanks to my js learning sessions with Romain, who is also responsible for a great majority of the js code below :)

Here’s the code, split into well defined components as we’ve been treating javascript as a first class language. It has also been unit tested, but I'll save that for another post.

Http


A wrapper around jquery ajax

var Async = Async || {};
Async.Http = function() {

function get(url, data, successHandler, errorHandler) {
$.ajax({
url: url,
data: data,
dataType: 'json',
contentType: 'application/json; charset=utf-8',
async: false,
success: successHandler,
error: errorHandler
});
}

function post(url, data, successHandler, errorHandler) {
$.ajax({
type: 'POST',
url: url,
data: JSON.stringify(data),
dataType: 'json',
contentType: 'application/json; charset=utf-8',
success: successHandler,
error: errorHandler
});
}

return {
get: get,
post: post
};

};

Notifier


A stateful js component that keeps track of all the ajax calls and adds a div to the dom with that information.

var Async = Async || {};
Async.Notifier = function() {

var $notifierElement = $('

');
$('body').append($notifierElement);

var inProgress = 0;

function start() {
++inProgress;
$notifierElement.removeClass('done');
}

function finished() {
--inProgress;
if (inProgress === 0) {
$notifierElement.addClass('done');
}
}

return {
start: start,
finished: finished
};

};

HttpAsyncDecorated


A decorated version of Async.Http that uses Notifier.

var Async = Async || {};
Async.HttpAsyncDecorated = function(http, asyncNotifier) {

function decorateWithFinish(handler) {
return function(data, textStatus, jqXHR) {
handler(data, textStatus, jqXHR);
asyncNotifier.finished();
};
}

function get(url, data, successHandler, errorHandler) {
asyncNotifier.start();
http.get(url, data, decorateWithFinish(successHandler), decorateWithFinish(errorHandler));
}

function post(url, data, successHandler, errorHandler) {
asyncNotifier.start();
http.post(url, data, decorateWithFinish(successHandler), decorateWithFinish(errorHandler));
}

return {
get: get,
post: post
};

};

JS App Context (Wiring things together)


Here’s the js code that glues everything together… Like a “javascript app context”.

var MyAppMain = MyAppMain || {};
MyAppMain.Context = function() {
function startup() {
var http = Async.HttpAsyncDecorated(Async.Http(), Async.Notifier());
MyApp.SomethingThatUsesAjaxHttp(http);
}

return {
startup: startup
};
};

WebDriver


And finally we need to use webdriver to wait for the div with the class asyncNotifier. We used WebDriverWait for that.

public static void waitForAsyncCallsToFinish(WebDriver driver)
{
waitForCssClass(driver, By.id("asyncNotifier"), "done");
}




Share this story