Pivotal Labs

Main menu

Skip to primary content
Skip to secondary content
  • About
  • Case Studies
  • Team
    • Executives
    • Locations
      • San Francisco (HQ)
      • Boston
      • Boulder
      • Denver
      • London
      • Los Angeles
      • New York
  • Community
    • Blogs
    • Tech Talks
    • Events
  • Careers
    • Lifestyle
    • Principles & Practices
    • Benefits
    • FAQ
    • Apply
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker

Taming JavaScript in practice: AJAX

Edward Hieatt
Monday, June 18, 2007

Commonly the JavaScript side of AJAX ends up untested, tightly coupled to the server-side code, and difficult to read. In a previous post, we saw how testability led to the ability to refactor our code to make it readable. This time we’ll focus on ways to test AJAX (which is a fairly lengthy topic in itself) and take it as read that once our code is tested we’ll be in a good position to refactor towards readability and to generally tame the complexities of client-side AJAX code.

Example

Suppose we have a very simple calculator application: the user enters a number into a textfield and presses a button labeled “Double”. The application sends the number to the server, and the server sends back a response containing twice the value of the number. The doubled value is then displayed in a second textfield. Of course, this is an artificial example, but the structure of the code is common enough to demonstrate practices for testing AJAX. Let’s use raw XmlHttpRequests here; I’ll post again separately with how to adapt this if you’re using prototype.js. Our code might look like this:

<script language="javascript">
var request;
function calculate() {
  var enteredValue = document.entryfield.value;
  if (window.XMLHttpRequest)
    request = new XMLHttpRequest();
  else
    request = new ActiveXObject("Microsoft.XMLHTTP");
  request.onreadystatechange = callback;
  request.open("GET", "/calculate?value=" + enteredValue, true);
  request.send(null);
}

function callback() {
  if (request.readyState == 4) {
    if (request.status != 200) {
      document.outputfield.value = "Error: " + request.status;
      return;
    }
    var responseValue = request.responseText;
    document.outputfield.value = responseValue;
  }
}

</script>

<input type="text" name="entryfield">
<input type="button" onclick="calculate()" value="Double">
<input type="text" name="outputfield">

When the user presses the “Double” button, an AJAX request is sent to a servlet, passing the value the user entered in the “entryField” text field. When the server responds, the text in the response is displayed in the “outputField” text field. If the server didn’t respond successfully, we display an error message in “outputField” containing the error code.

Starting a Test Page

Let’s try to write a Test Page for our code:

<script language="javascript" src="/path/to/calculate.js"></script>
<script language="javascript">
function testClickCalculate() {
  document.entryfield.value = "5";
  calculate(); //but wait - we don't have a server
  //now what?
}
</script>

<input type="text" name="entryfield">
<input type="text" name="outputfield">
</body>

Hmm – how do we proceed? As things are, when we run our test, our request will get sent off to a server that isn’t running. We don’t have enough control over our environment to continue with the test.

So, how can we test AJAX?

The most important thing to bear in mind is that we are trying to write unit tests for our JavaScript – tests that exercise just a unit of our JavaScript code at a time. So we certainly don’t want to bring a server into the picture – that’s way out of scope for our JavaScript unit tests. Instead, what we want to do is insulate ourselves from the machinery of the request/response server interaction. The typical point at which we set up our insulation is at the level of the XmlHttpRequest: rather than a real request, we will use a mock version in our test – a pretend version of the request that we control.

Testing the request

Our first task, then, is to set things up so that our test uses a mock request and our code uses a real one:

function calculate() {
  ...
  request = createRequest();
  ...
}

function createRequest() {
  if (window.XMLHttpRequest)
    return new XMLHttpRequest();
  else
    return new ActiveXObject("Microsoft.XMLHTTP");
}

but in our Test Page, we implement createRequest differently. JsUnit comes with a library called jsUnitAjax.js, which contains a mock implementation of XmlHttpRequest.

<script language="javascript" src="/path/to/jsunit/lib/jsUnitAjax.js"></script>

function createRequest() {
  return new MockXmlHttpRequest();
}

Our Test Page’s implementation overrides the real implementation of createRequest. Good: now we are able to call calculate without worrying about a real request getting sent to the server. Let’s go back and continue with our test.

function testClickCalculate() {
  document.entryfield.value = "5";
  calculate();
  assertEquals("GET", request.method);
  assertEquals("/servlet?value=5", request.url);
  assertTrue(request.isAsync);
  assertTrue(request.sendCalled);
  assertNull(request.data);
  assertEquals(callback, request.onreadystatechange);
}

Notice that we’re testing that calling calculate() sends the request, and how the request gets set up – its method, its URL, etc – by examining the mock request. We also verify that the correct callback method has been set up for when the server responds. We aren’t using a real server, and we don’t care (in this test) about a response.

Testing the response

So, that’s half the story. How about testing the response? We need to simulate the server responding to the request. We’ve already tested that the correct callback is set up; let’s take advantage of that now.

function testValidServerResponse() {
  request = new MockXmlHttpRequest();
  request.readyState = 1;
  callback();
  assertEquals("", document.outputfield.value);

  request.readyState = 2;
  callback();
  assertEquals("", document.outputfield.value);

  request.readyState = 3;
  callback();
  assertEquals("", document.outputfield.value);

  request.readyState = 4;
  request.status = 200;
  request.responseText = "50";
  callback();
  assertEquals("50", document.outputfield.value);
}

We go through each readyState, ensuring that nothing happens until state 4. For state 4, we give the mock request a status of 200 and a responseText of “50″, and then verify that calling callback() populates the outputfield correctly. Notice how we’ve tested just the response logic, without using a real server and without the need to set up a meaningful request.

Simulating a server-side error

We have one more test – we need to test what happens when the server responds unsuccessfully:

function testInvalidServerResponse() {
  request = new MockXmlHttpRequest();
  request.readyState = 4;
  request.status = 500;
  callback();
  assertEquals("Error: 500", document.outputfield.value);
}

This time we give the mock request a status of 500, and we verify that the output field contains the expected error message.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

7 Comments

  1. Frank Manno says:

    Just came across this article, and love how you explain the mock request/response concepts!

    If we wanted to test actual mock objects from our response (ie: a JSON object), how much different would the setup be?

    December 12, 2007 at 11:50 pm

  2. Edward Hieatt says:

    You can make the mock response contain anything you like. So, if your real response is JSON, just make it be the JSON (as a string).

    December 12, 2007 at 11:50 pm

  3. Daya says:

    Hi Edward

    In your blog you mentioned jsUnit comes with a library called jsUnitAjax.js , but that is not the case, atleast not here https://sourceforge.net/project/showfiles.php?groupid=28041&packageid=19823&release_id=404277

    Is MockXmlHttpRequest proprietary to Pivotal?

    Could u please point me to the right source.?

    thanks
    -daya

    December 12, 2007 at 11:50 pm

  4. Edward Hieatt says:

    Sorry, I should have mentioned that it’s not in the released version on SourceForge.net – it’s in the latest tagged version in SVN. We’re planning on releasing it soon (as part of the JsUnit 2.2 release).

    December 12, 2007 at 11:50 pm

  5. Daya says:

    Could you please email me the library jsUnitAjax.js at daya dot sharma at gmail dot com

    thanks

    December 12, 2007 at 11:50 pm

  6. Dan says:

    Wow, Incredible Work – Thanks!

    Here’s the code to inject the mock into ExtJS, which is my weapon of choice:

    Ext.lib.Ajax.createXhrObject = function(transactionId) {
    return{conn:mockRequest, tId:transactionId};
    }

    Dan

    Ps. ^ I do wish these idiots wouldn’t post this rubbish in your comment system.

    December 12, 2007 at 11:50 pm

  7. Edward Hieatt says:

    Daya – check out HEAD from SourceForge, or see here:

    http://jsunit.svn.sourceforge.net/viewvc/jsunit/trunk/jsunit/lib/

    December 12, 2007 at 11:50 pm

Add New Comment Cancel reply

Your email address will not be published.

Edward Hieatt

Edward Hieatt

As Chief Operating Officer, Edward Hieatt oversees all operational aspects of Pivotal Labs’ consulting business including engineering, client services and the company’s open source product strategy. In this role, he drives revenue growth and performance, and is responsible for managing all regional offices across the US and overseas.

Edward ensures that Pivotal’s development teams identify, anticipate and exceed client expectations for their products. His engineering organization is acclaimed for delivering rapid, high-quality, sustainable, iterative development services.

With experience as a software developer and an engineering leader responsible for delivering web and mobile applications as well as large-scale enterprise infrastructure projects, Edward brings deep insights to the COO position. After joining Pivotal as Principal in 2003, Edward was responsible for leading dozens of client engagements, and was instrumental in defining Pivotal’s development process and methodologies. Edward was promoted to Vice President, Engineering in 2008, and subsequently oversaw Pivotal's expansion into the New York and Boulder, CO, markets. Edward was also a key contributor to the development of Pivotal Tracker, the agile project management and collaboration tool used by thousands of software developers worldwide.

Edward holds a degree in Mathematics from Oxford University, England.

Edward's Blog

Recent Posts

  • New Product Management and Design Offerings from Pivotal Labs
  • What’s Your Start-up’s "Bus Count"?
  • CIMonitor (née Pulse)
Subscribe to Edward's Feed

Author Topics

Ideas (1)
award (2)
javascript (6)
jsunit (6)
agile (8)
mobile (1)
webos (1)
gogaruco (2)
conference (2)
ruby (2)
ide (1)
rails (1)
conferences (1)
python (1)
ajax (1)
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Contact
  • Labs
  • Events

Contact Us

contact@pivotallabs.com
+1 415-77-PIVOT
TwitterLinkedInFacebook

Pivotal Tracker

Tracker is the award-winning agile project management tool that enables real-time collaboration around a shared, prioritized backlog.
Visit pivotaltracker.com >