Web Testing with MbUnit and WatiN Part 3: Testing Asynchronous AJAX Calls

An asynchronous HTTP request (an AJAX request) is nothing more than another HTTP request. The difference however is that it returns immediately. It does not wait for the server to send a response, which will be eventually caught at a later stage of page life-cycle.

In the third and final episode of our series of articles, we will focus on testing asynchronous HTTP requests coming from

  • An ASP.NET UpdatePanel web control
  • jQuery AJAX methods

If you have already read and followed part one or part two of this tutorial, you will already have all you need to follow this part. If not, you will need the following installed.

  • Visual Studio 2010 or later and .NET 4.0 or higher. You can install the free Express edition of Visual Studio 2010 here.
  • Gallio \ MbUnit. The latest release is v3.3.442 and can be downloaded here as either MSI installer or zipped binary files. The Gallio wiki covers the installation process for both.
  • The MbUnit.Web helper project that comes as a part of the Web Testing with MbUnit and WatiN sample code project on GitHub. This contains the sample code for this article.
  • WatiN. The latest release is v2.1.0.1196 and can be downloaded here.

It is also assumed that you know how to run tests with Gallio. If you don’t, this short tutorial in the Gallio wiki is a good place to start before carrying on.

Handling Asynchronous Requests

Let’s consider a simple web page containing a button which triggers an HTTP request (for example a form submit). When you write a web test and use WatiN to emulate a user click, you invoke the method WatiN.Core.Button.Click(). This method returns once the whole HTTP request round trip is complete. In the case of a traditional synchronous post-back request, it means that the Click() method returns once the new web page was received, loaded, and displayed in the browser.

However, because AJAX requests are, by defintion, asynchronous, the Click() method returns immediately when it invokes them, without waiting for the answer. If you rely on AJAX features in your web pages then, it’s likely that your web tests will behave strangely if you do not expect Click() to return before the asynchronous call actually updates the page.

The MbUnit.Web project provides a few helpful methods to ease testing AJAX-enabled web pages. It supports two distinct techniques, but basically the principle is the same for both. After the AJAX request is submitted, we need to call an extra method which monitors the current running asynchronous request(s) and waits until they have all been completed.

Note that the sample code for this article forms part of the SampleWebApplication project that comes as a part of the Web Testing with MbUnit and WatiN sample code project on GitHub.

Testing Calls From An UpdatePanel Control

The UpdatePanel web control is usually the preferred way to submit asynchronous requests in ASP.NET web forms applications. Once rendered in a browser, an UpdatePanel control exposes several handy properties and methods for use in your own JavaScript code. In the context of testing AJAX requests, we can use the isInAsyncPostBack() method on the PageRequestManager object to monitor asynchronous activities and check if an AJAX postback is currently in progress.

Combining this method with WatiN’s Browser.Eval() method to make the current browser instance evaluate JavaScript code on the fly means that we have a simple way to make test code wait for a while until an AJAX request has completed.

protected void WaitForAsyncPostBackComplete()
{
   switch (BrowserContext.BrowserConfiguration.BrowserType)
   {
      case BrowserType.IE:
      {
         Wait(() => !Convert.ToBoolean(Browser.Eval  
            ("Sys.WebForms.PageRequestManager.getInstance().get_isInAsyncPostBack();")));
         break;
      }
   }
}

WaitForAsyncPostBackComplete() is part of the AbstractBrowserTestFixture class in the MbUnit.Web project so you need only make a couple of chages to our test code to accommodate the testing of AJAX requests.

  1. Have the TestFixture class inherit from MbUnit.Web.AbstractBrowserTestFixture
  2. Have your test call WaitForAsycPostBackComplete after an expected asynchronous submission to let the test re-synchronize with the AJAX-enabled page.

For example.

[TestFixture]
public class DefaultPageTest : AbstractBrowserTestFixture<TPage>
{
   [Test, RunBrowser]
   public void Async_postback_with_UpdatePanel()
   {
      var page = GoToPage();
      page.TextBoxName.TypeText("McFly");
      page.ButtonOkAsync.Click();
      WaitForAsyncPostBackComplete(); // Wait for AJAX requests to complete
      Assert.AreEqual(page.SpanResultAsync.Text, "Hello from async postback, McFly.");
      Assert.IsNull(page.SpanResultSync.Text);
      Assert.IsNull(page.SpanResultAsyncJQuery.Text);
   }
   …
}

jQuery AJAX

MVC-based web applications using ASP.NET MVC or any other similar frameworks are more likely to use a third-party API such as jQuery to send asynchronous requests. jQuery does not have any built-in property to monitor running AJAX requests. However it does provide a few hooks that we can use to instrument the page.

function AjaxMonitorForWatiN() {
  var count = 0;
  $(document).ajaxStart(function() { count++; });
  $(document).ajaxComplete(function() { count--; });
  this.asyncInProgress = function () { return (count > 0); }; 
}

var ajaxMonitorForWatiN = new AjaxMonitorForWatiN();

This short JavaScript snippet instantiates a simple monitoring tool that keeps track of running AJAX requests. It’s quiet rudimentary but efficient enough for our testing purpose.

Of course we need to “inject” that code inside the page we want to test and as luck would have it, the AbstractBrowserTestFixture class includes the StartJQueryAjaxMonitoring() method to do just that.

protected void StartJQueryAjaxMonitoring()
{
   Browser.Eval(
      "function AjaxMonitorForWatiN() {" +
      " var count = 0;" +
      " $(document).ajaxStart(function() { count++; });" +
      " $(document).ajaxComplete(function() { count--; });" +
      " this.asyncInProgress = function () { return (count > 0); }; }" +
      "var ajaxMonitorForWatiN = new AjaxMonitorForWatiN();");
}

To make use of this code, you must call StartJQueryAjaxMonitoring() as soon as the page is called, otherwise we could miss some asynchronous requests.

[Test, RunBrowser]
public void Async_postback_with_jQuery_Ajax()
{
   var page = GoToPage();
   // Attach a monitor to count async jQuery/AJAX requests in progress.
   StartJQueryAjaxMonitoring(); 

In a similar fashion to what we have done for monitoring UpdatePanel AJAX requests, it’s now possible to call a helper method - WaitForJQueryAjaxComplete() - from the abstract test fixture and make it wait if any asynchronous request is still running.

   page.TextBoxName.TypeText("McFly");
   page.ButtonOkAsyncJQuery.Click();
   WaitForJQueryAjaxComplete(); // Wait for all jQuery/AJAX requests to complete.
   Assert.AreEqual(page.SpanResultAsyncJQuery.Text, "Hello from async postback, McFly.");
   Assert.IsNull(page.SpanResultSync.Text);
   Assert.IsNull(page.SpanResultAsync.Text);
}

Much like the WaitForAsyncPostBackComplete() method we saw earlier, WaitForJQueryAjaxComplete() uses WatiN’s Browser.Eval method to query the simple monitor we injected into the page earlier in the test.

protected void WaitForJQueryAjaxComplete()
{
   Wait(() => !Convert.ToBoolean(
      Browser.Eval("ajaxMonitorForWatiN.asyncInProgress()")));
}

The sample DefaultPageTest fixture summarizes all those different techniques and shows how to use them in an efficient way.

Sometimes the manual registration of the jQuery/AJAX monitor may be problematic though. This might be an issue for instance if the scope of your test causes several pages to appear sequentially, or if an AJAX request is immediately triggered when the page is displayed. It’s then possible that the instrumentation of the page occurs after some requests have been issued.

If such a case happens, you may prefer to move the instrumentation to a lower level: for example directly in your View or Controller base class. Ideally, the instrumentation should be only necessary if HttpContext.IsDebuggingEnabled is true. This will reduce the little overhead introduced by this extra functionality.

Summary

In this article, we’ve looked at the main problem with testing asynchronous (AJAX) calls from a web page to a server using both the UpdatePanel control and jQuery’s own AJAX methods. We saw how MbUnit.Web.AbstractBrowserTestFixture contains the WaitForAsyncPostBackComplete and the WaitForAjaxQueryComplete methods for use in this situation and what they do.

You might also like...

Comments

About the author

Yann Trevin

Yann Trevin Luxembourg

Yann is a software engineer working at the research center of a large tire manufacturing company. He's mostly involved in web, desktop and mobile enterprise projects built on the .NET stack. He ...

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Before software should be reusable, it should be usable.” - Ralph Johnson