Salient Solutions

wrasslin ones and nones for fun and profit - Sky Sanders' Blog
Get your own ranked flair here
posts - 92, comments - 103, trackbacks - 0

JsUnitTest Async Test Pattern

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: , ,

Print | posted on Wednesday, August 19, 2009 10:41 PM |

Feedback

No comments posted yet.

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 5 and 1 and type the answer here:

Powered by: