Isolating/Testing Legacy Javascript Code (w/ jquery’s bind & trigger)

So I started writing some javascript code. Didn’t test it (tsk tsk). Then I started writing tests.

Then a user reported a bug in an untested part. Since I am writing tests now I resist the urge to fix it quickly, and take the time to understand the problem.

Here is how our little regression happened. First I had this little function that ran when the user clicked the submit button. It did some “stuff” and then submitted the form.

submitVafForm = function()
{
    if( jQuery('#categorySelect').val() != '?' )
    {
        jQuery('#vafForm').attr( 'action', jQuery('#categorySelect').val() );
    }
    document.vafForm.submit();
}

Basically this element “categorySelect” contained the URL that the form should post to. But then some users were saying it did not work (when the form got submitted the “meta data” I was storing in that select box was messing up the GET parameters. No problem I though, I’ll just add one last line of jquery to clear out the value after I’m done reading it:

jQuery('#categorySelect').html('


');

Boom. Job done. Right? I pushed the code.

But then I forgot about the fact that some users do not use this category chooser. In that case a hidden element was placed on the page so there would be a “NULL” value. However in Internet Explorer only, trying to call .html() on a hidden element threw a javascript error and crashed my application.

Hmmm great. That’s what I get for not writing a test for that. So once I am to this point how to dig myself out of the hole I have dug myself in?

Jquery’s bind & trigger. You see I can easily write a unit test that replicates the issue, all I’d have to do is click that button, I don’t even need an assertion:

$("#vafSubmit").trigger("click");

Done. Function gets called. Only problem… the test page actually gets redirected because the form submit is actually happening.

This is where javascript really really shines. How do we isolate that single .submit() line from the rest of the code? I want to test that part, without that last line running. Well I could use lambda, but I choose a more natural approach. Jquery’s bind & trigger functions.

You see we can just extract the bit we don’t want running during test into an event handler:

submitVafForm = function()
    {
        if( jQuery('#categorySelect').val() != '?' )
        {
            jQuery('#vafForm').attr( 'action', jQuery('#categorySelect').val() );
        }
        var chooser = jQuery('#categorySelect');
        if( !chooser.is('input') ) {
            chooser.html('


');
        }
        jQuery().trigger("vafSubmit");
        return false;
    }

    jQuery().bind("vafSubmit", function() {
        document.vafForm.submit();
    })

So now instead of submitting the form we fire an event. By default that event is bound to the anonymous function that submits the form. During test we can unbind this event so that code does not fire.

jQuery().unbind("vafSubmit");

And there you have it. In an OOP language with inheritence we would simply create the concept of a “test specific sub class”. In jquery we have events which are a better solution in my opinion because it results in a cleaner separation of test code from production code.

This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>