Nate's Programming Blog

A few weeks ago I was attempting my first TFD javascript application for a presentation I was doing on OO Javascript. I was going to write an inventory application that loads inventory items from an xml file and displays them in a table on the page. I had run into some limitations before, where I was unable to test any of the asynchronous code (XMLHttpRequest, setTimeout, setInterval) because the tests would complete before the asynchronous code was called. The reason for this is that when you have an asynchronous fork in your Javascript code, the current process will go ahead and complete executing and then once it is done and the asynchronous process is ready to begin, it will execute. The problem with jsunit is that there is no way to tell the test to wait until the asynchronous functionality completes, therefore the test goes and completes and reports the results before the code actually finishes running.

Anyway, I really wanted to find a way around this problem, so I started digging in the tests for jsunit and found the setUpPage function. This is different from the setUp function. It allows you to set a global variable called setUpPageStatus to some arbitrary value, like “running” and then register some asynchronous functionality providing a callback to another function in your test like setUpPageComplete. Then that function changes the setUpPageStatus variable to complete telling jsunit that the page has been setup and the tests can begin.

Here is an example:

function setUpPage() {
setUpPageStatus = ‘running’;
setTimeout(setUpPageComplete, 1000);
}

function setUpPageComplete() {
if (setUpPageStatus == ‘running’) {
setUpPageStatus = ‘complete’;
}
}

function testOptionsWereLoaded() {
var options = document.getElementById(‘viewLog’).getElementsByTagName(‘option’);
assertEquals(‘Assert first option was created.’, ‘someValue’, options[0].value);
}

In this example, behind the scenes I’m loading some xml and populating a drop down list asynchronously upon page load. Therefore I have to wait until that completes before I can test that the drop down was correctly populated. So I setup a timeout to have jsunit wait for 1 second before it executes the tests. That gives the asynchronous code time to complete before I start verifying the document contents.

This solved the problem I was having at the time, but soon I ran into another issue. What if I have multiple asynchronous calls within a single javascript object. In that situation, I would have to write a separate jsunit test file for each asynchronous call. I didn’t like that solution because I felt it would make things confussing for someone else looking at the tests. Therefore I decided to modify jsunit to handle this case using the same pattern they used for setUpPage. I wanted to do it test first of course, so I wrote out some tests that would set a testStatus global and then call another method asynchronously to notify jsunit that the test had completed and to continue with the next test. I spent the morning on it and soon had something working. Now I have the ability to essentially pause a test during execution to give it time to complete any asynchronous activity and then complete the rest of the tests after it is done.

Here is an example:

var wasListenerCalled = false;

// Needed so that firefox executes the tests in the correct order.
function exposeTestFunctionNames() {
return [‘testRegisterListenerCallsListenerWhenLogFileListIsRetrieved’,
‘testListenerWasCalled’];
}

function testRegisterListenerCallsListenerWhenLogFileListIsRetrieved() {
testStatus = ‘running’;
var service = new MockService();
var model = new Lib.WST.Model.LogFileListModel(service, Lib.Utils.ObjectUtils);
model.registerListener(callListener);
model.loadLogFileList();
}

function callListener(logFiles) {
if (testStatus == ‘running’) {
testStatus = ‘complete’;
}
assertEquals(‘Assert 2 log files were passed to listener.’, 2, logFiles.length);
wasListenerCalled = true;
}

function testListenerWasCalled() {
assertTrue(‘Assert listener was executed.’, wasListenerCalled);
}

This block is testing the LogFileListModel object which retrieves and maintains the log file data. The flow here is it will call testRegisterListenerCallsListenerWhenLogFileListIsRetrieved first. It tests that other objects can register themselves with the model and after the model processes the XML representing the list of log files it will pass an array of strings to all listeners. I want to test that that actually occurs. The load method is asynchronous, therefore I need to pause execution until after the XML is retrieved and processed. Then verify that the array of log files was passed to the registered listener.

So I have my first test go ahead and setup the scenario, registering a listener that is defined by the callListener function and setting the testStatus to “running”. The callListener function gets called after the xml is loaded and converted into a string array. It sets the testStatus to “complete” notifying jsunit that the test is now done and verifies the info sent to the method. The last test is a safe guard to verify that callListener was called and was able to execute it’s assert.

I have to use the exposeTestFunctionNames method in this case so that I can define the order in which I want the tests to execute, otherwise firefox will execute them in reverse order of IE and the tests will fail in one and not the other.

I’m actually quite excited about this change. I feel much more confident in jsunit for providing me with everything I need to fully test any JavaScript application I write, which in turn allows me to use TFD in all future JavaScript applications.


This entry was posted on Monday, April 10th, 2006 at 8:18 pm and is filed under Javascript, Programming. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.
3 Comments so far

  1. James Estes on April 12, 2006 8:16 am

    Great Job! You might get on the mailing list and see what they think. I believe they are working hard on getting the next release out the door.

  2. VP on April 25, 2006 2:56 am

    This is exactly what I’m looking for. Do you have a patch for jsUnit online somewhere?

  3. Nate on May 2, 2006 4:48 pm

    No, I never got a chance to clean up the code and post it, but I did see on the jsUnit forum that they have done a lot of work with being able to do asynchronous tests and to check out the latest in CVS. I haven’t had a chance to, but if you are looking for something now you might check there.

Name (required)

Email (required)

Website

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Feel free to leave a comment

top