Lately I have been using a lightweight JavaScript testing framework called JsUnitTest.
It is quite easy to use but the documentation is non-existent. This is a situation I have begun to remedy by vs-doccing my copy. I may post it soon.
In any case, my cursory investigation did not uncover an established async pattern so I wrote a song about it. Wanna hear it? Here it goes........
Listing 1: jsunittest async patternĀ
/*
Jsunittest async runner pattern by Sky Sanders
http://skysanders.net/subtext/archive/2009/08/19/jsunittest-async-test-pattern.aspx
This pattern enhances standard test runs with the ability to 'pause' the run
until the currently running case signals completion.
If a case is synchronous, nothing need be done.
If a case is to be asynchronous, simply set a .pending flag on the case. (this
or the reference var tcase).
When teardown is called, if the pending flag is set, an idle loop is initiated
that will continue until the pending flag on the case is set false.
Presented here is the bare pattern. Careful consideration should be given to the
positioning of the flag set expression to prevent a hung test run.
It is my experience that you should set pending true as late as possible but BEFORE
initiating any action that may result in pending being set false.
e.g. immediately before sending an xhr request whose onreadystatechange contains
the un-flagging expression. (see example)
At first blush, a timeout seems like a worthy addition. It is my contention that
if I need a timeout for my test case then my test case is broken. ;-)
But that's just me.
*/
function suspend()
{
/*
if suspend is called by a testcase that has a positive
.pending property an idle loop will be initiated that
will continue until the .pending property is set false.
*/
if (this.pending)
{
this.wait(10, suspend);
}
}
new Test.Unit.Runner({
// our 'this' context reference
tcase: null,
setup: function()
{
/*
get our obligator 'that=this' for the closures
that are ever so present in async code
*/
tcase = this;
},
teardown: function()
{
/*
if the case has been flagged as pending, start
the idle loop.
*/
suspend.call(this);
},
// begin tests
test_sync_01: function()
{
this.assert(true);
},
test_async_01: function()
{
// simulate an ajax call
function readystatechange()
{
tcase.assert(true);
/*
un-flag the case as pending and in +/- 10ms
the next case will start
*/
tcase.pending = false;
}
/*
flag as pending just before any action that may result in pending being
set false or you are asking for a hung test run
*/
this.pending = true;
// make the call
window.setTimeout(readystatechange, 1000);
},
test_sync_02: function()
{
this.assert(true);
},
test_async_02: function()
{
function readystatechange()
{
tcase.assert(true);
tcase.pending = false;
}
this.pending = true;
window.setTimeout(readystatechange, 1000);
},
test_async_03: function()
{
function readystatechange()
{
tcase.assert(true);
tcase.pending = false;
}
this.pending = true;
window.setTimeout(readystatechange, 1000);
}
});
Technorati tags:
JavaScript,
AJAX,
Unit Testing