Archive

Archive for the ‘jQuery’ Category

Synchronizing WatiN and Ajax with jQuery

March 6, 2011 6 comments

WatiN (pronounced “what in”) is an open source .Net project that allows the programmatic automation of browsers (IE/Firefox/Chrome).  My company uses WatiN within Visual Studio unit tests to automate the testing of our web application.  It works interactively with the browser window: clicking, entering text, and navigating just like a user.   With the unit tests, assertions can be made about the state of the application through various user scenarios.  The project is under active development on sourceforge.  In short, it’s a wonderful tool and isn’t hard to use.

We maintain hundreds of test cases against our web application.  We chain team builds so that when the app build finishes building successfully, it kicks off another build which ships and runs mstest against the WatiN solution.  There’s a dedicated build agent running in interactive mode which runs the tests.  I’ll cover this setup in a future post.

WatiN abstracts the browser/DOM/javascript engine into its own API.  It provides a consistent abstraction for several major browsers.  At a high level, it works by instantiating the browser under test and holding a native windows handle to the browser window.  Then unmanaged calls into User32.dll are used to perform various actions on the browser.

There’s a good amount of synchronization that is done natively by WatiN.  For example, when you click a link that goes to a new page, the click is simulated and then the WatiN code waits for the browser to be ready when the next page is loaded.  In most cases this works perfectly.  Here’s an example from the WatiN homepage:

[Test]
public void SearchForWatiNOnGoogle()
{
  using (var browser = new IE("http://www.google.com"))
  {
    browser.TextField(Find.ByName("q")).TypeText("WatiN");
    browser.Button(Find.ByName("btnG")).Click();

    Assert.IsTrue(browser.ContainsText("WatiN"));
  }
}

Between the .Click() method and the Assert, WatiN waits for the DOM to complete loading.  See the DomContainer.WaitForComplete() method in WatiN to understand how this works.

However, modern web applications change their pages with Ajax in addition to full page loads.  This can been a hairy problem for every browser automation library, including WaitN.  WatiN doesn’t know which page interaction will trigger Ajax behavior, and the page may be different after the interaction.  Another synchronization mechanism in needed.

If you are using jQuery .ajax, you’re in luck.

jQuery has global Ajax event handlers ajaxStart and ajaxStop which wrap all ajax activity.  Any time Ajax requests start, ajaxStart is called; and after all ajax requests have finished, ajaxStop is called.  We can write a piece of code in a central place (common js or masterpage) to keep track of whether the page is currently in the middle of an ajax request.

// used to syncronize the WaitN tests
var isRunningAjax = false;
$(document).ajaxStop(function() { isRunningAjax = false; });
$(document).ajaxStart(function() { isRunningAjax = true; });

Now we’ll give WatiN  the ability to poll the page for the isRunningAjax variable, using its ability to run any javascript with the Browser.Eval method.  We do this with a simple extension method on Browser, which is written in the same spirit as WatiN’s native WaitForComplete():


internal static void WaitForAjaxComplete(this DomContainer browser)  {            
  WaitForAjaxComplete(browser, 150);        
}

internal static void WaitForAjaxComplete(this DomContainer browser, int pollingIntervalInMs) {
  while (true)
  {                
    Thread.Sleep(pollingIntervalInMs);                                    
    if(browser.Eval("typeof(isRunningAjax)") == "undefined" || browser.Eval("isRunningAjax") == "false") { 
      break;
    }
  }        
}

Normally, a while(true) would be a no-no in production code, but in this case since the mstest runner has a timeout (default 30s) for the test to run, the worst that can happen is 30 seconds of waiting followed by a failed test. Also, notice that typeof(isRunningAjax) makes sure the synchronization mechanism is on the page (in case you forget to put it there, or another js error prevented its evaluation).

Finally, here’s a snippet of WatiN code that takes advantage of our new synchronization for jQuery Ajax calls:

 Browser.TextField(Find.ById(SOME_ID)).TypeText(SOME_SEARCH_KEY);
 Browser.WaitForAjaxComplete();
 Div div = Browser.Div(Find.ByClass("savedResultsBlock"));
 div.Link(Find.ByTitle(SOME_VALUE)).Click();

I’ve been in awe of WatiN for a while now and I’m planning some more posts describing how we have set up a page/control wrapper to WatiN that provides an even higher layer of abstraction so that the test methods can be written against an Api specific to our application.

Categories: .Net, jQuery, Uncategorized, WatiN