Mark Needham

Thoughts on Software Development

Selenium: Waiting for jQuery AJAX calls

with 4 comments

Lu Ning and I have been working on trying to improving the consistency of our selenium driven acceptance test build, the main problem being the number of false negatives we've been getting.

The reason for these false negatives has been due to the difficulty in working out the necessary condition to wait for when making ajax calls using the jQuery library.

The conditions can be a combination of elements on the page appearing, disappearing, being enabled or disabled and sometimes we don't actually have any visible change on the page which we can hook into to know whether or not an ajax call has returned.

We tried several different solutions to this problem, each one resulting in less and less code until we came across some jQuery ajax hooks which we weren't previously aware of.

The events we looked at were ajaxComplete, ajaxError, ajaxSend, ajaxStart, ajaxStop and ajaxSuccess which don't seem to fire exactly when you might expect.

We had thought that we might just be able to listen to ajaxStop and then we would know when all the ajax calls on the page had completed but this didn't seem to fire all the time. A couple of the other events were equally troublesome but we've ended up with the following code to allow us to tell when ajax requests have returned so we can continue with the next selenium command in our test.

// extend jQuery ajax with the capability of remembering the count of active ajax requests
$.activeAjaxRequestCount = 0;
 
$().ajaxSend(function() {
  $.activeAjaxRequestCount++;
  // register lazily to make sure it's the last one and get executed after all other handlers
  if (!$._ajaxErrorHandlerAdded) {
    $().ajaxError(function() {
      $.activeAjaxRequestCount--;
    });
    $._ajaxErrorHandlerAdded = true;
  }
})
.ajaxSuccess(function() {
  $.activeAjaxRequestCount--;
});

We have some customised error handling in our code which already hooks into the ajaxError event so we wanted to make sure that we could add a handler which was fired after all the production code handlers had completed.

We originally tried just directly hooking up a handler to the ajaxError event inside the document ready but we found that this handler was getting fired before the customised error handling one.

Lu therefore came up with the idea of creating this handler when we send our first ajax request which means that it will be the last handler fired when an error is raised.

We created a decorator around the selenium client in order to allow us to use this code from our C# code:

public class SeleniumDecorator : ISelenium 
{
	public void Click(string locator, int timeout)
	{
      	    WaitComplete(() => selenium.Click(locator), timeout);
	}
 
	private void WaitComplete(Action fn, int timeout)
	{
  		selenium.RunScript("selenium.browserbot.getCurrentWindow();window.jQuery.activeAjaxRequestCount=0;");
  		fn();
  		WaitForCondition("selenium.browserbot.getCurrentWindow();window.jQuery.activeAjaxRequestCount===0;", timeout.ToString());
	}
}

We are also wrapping other selenium calls which might trigger ajax calls which include 'FireEvent', 'Type' and 'Select'.

The other clever thing which Josh discovered here is that to access jQuery libraries from selenium we need to refer to 'window.jQuery' – we then seem to have access to any jQuery related code that we have loaded on the page.

I've discussed with a couple of colleagues the merits of doing this and it was pointed out to me that it's not generally a good idea to put code in just to make something more testable at the acceptance level.

In this case we are using the activeAjaxRequestCount in production code for being able to monitor the state of our ajax requests so it does have another use as well.

Written by Mark Needham

May 14th, 2009 at 7:06 pm

4 Responses to 'Selenium: Waiting for jQuery AJAX calls'

Subscribe to comments with RSS or TrackBack to 'Selenium: Waiting for jQuery AJAX calls'.

  1. Selenium: Waiting for jQuery AJAX calls – Mark Needham…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

    DotNetShoutout

    16 May 09 at 6:49 am

  2. [...] Selenium: Waiting for jQuery AJAX calls at Mark Needham [...]

  3. Thanks for the info on hooking into jQuery's ajax calls… exactly what I needed to create a global "Loading" message at the top of a site whenever an AJAX call is processed

    Matt

    29 Jul 09 at 8:03 am

  4. Hi,

    I have seen many people use a counter to track when AJAX calls are initiated and completed. The problem is, you cannot simply say, 'if the counter equals zero all calls have finished', because it could be the case that call 1 finishes before call 2 has begun, couldn't it?

    I am really struggling with this. Everyone seems to find no problem with the simple counter solution. I don't see how it works all the time.

    Thanks,

    Mike

    Michael

    12 Oct 09 at 7:45 pm

Leave a Reply