Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test Javascript code that interacts with DOM elements

Background:

I'm coming from Java background so not too familiar with Javascript.

We are planning to introduce JavaScript unit testing to both our existing (legacy) code and future work. We are a java shop (Spring, Weblogic etc) mainly.

We are looking at options that give us good integration with IDE (IntelliJ idea) and sonar, as well as being able to run them as part of continuous integration.

JsTestDriver seems to tick all the boxes.

Question:

A lot of our existing javascript code is a)embeded within JSPs and b)utilises jQuery to interact with page elements directly.

How should we go about testing a function that is heavily reliant on the DOM. Here's some code examples of functions that I'm talking about:

function enableOccupationDetailsText (){
    $( "#fldOccupation" ).val("Unknown");
    $( "#fldOccupation_Details" ).attr("disabled", "");
    $( "#fldOccupation_Details" ).val("");
    $( "#fldOccupation_Details" ).focus();
}

or

jQuery(document).ready(function(){

    var oTable = $('#policies').dataTable( {
            "sDom" : 'frplitip',
                "bProcessing": true,
                "bServerSide": true,
                "sAjaxSource": "xxxx.do",
                "sPaginationType": "full_numbers",
                "aaSorting": [[ 1, "asc" ]],
                "oLanguage": {
                    "sProcessing":   "Processing...",
                    "sLengthMenu":   "Show _MENU_ policies",
                    "sZeroRecords":  "No matching policies found",
                    "sInfo":         "Showing _START_ to _END_ of _TOTAL_ policies",
                    "sInfoEmpty":    "Showing 0 to 0 of 0 policies",
                    "sInfoFiltered": "(filtered from _MAX_ total policies)",
                    "sInfoPostFix":  "",
                    "sSearch":       "Search:",
                    "sUrl":          "",
                    "oPaginate": {
                        "sFirst":    "First",
                        "sPrevious": "Previous",
                        "sNext":     "Next",
                        "sLast":     "Last"
                }
            },
                "fnRowCallback": function( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
                        $('td:eq(0)', nRow).html( "<a href='/ole/policy/general_details.do?policy_id="+aData[0]+"'>"+aData[0]+"</a>" );
                        return nRow;

                },

                "fnServerData" : function ( url, data, callback, settings ) {

                settings.jqXHR = $.ajax( {
                    "url": url,
                    "data": data,
                    "success": function (json) {
                        if (json.errorMessage != null) {
                            var an = settings.aanFeatures.r;
                            an[0].style.fontSize="18px";
                            an[0].style.backgroundColor="red";
                            an[0].style.height="70px";
                            an[0].innerHTML=json.errorMessage;
                            setTimeout('window.location="/xxxx"',1000);
                            //window.location="/xxxxx";
                        } else {
                            $(settings.oInstance).trigger('xhr', settings);
                            callback( json );
                        }
                    },
                    "dataType": "json",
                    "cache": false,
                    "error": function (xhr, error, thrown) {
                        if ( error == "parsererror" ) {
                            alert( "Unexpected error, please contact system administrator. Press OK to be redirected to home page." );
                            window.location="/xxxx";
                        }
                    }
                } );
                }

            } );
        $("#policies_filter :text").attr('id', 'fldKeywordSearch');
        $("#policies_length :input").attr('id', 'fldNumberOfRows');
        $("body").find("span > span").css("border","3px solid red");
        oTable.fnSetFilteringDelay(500);
        oTable.fnSearchHighlighting();
        $("#fldKeywordSearch").focus();

}
);

In the latter case, my approach would be that the function in question is way too large and should be broken to smaller (units) so that it can be tested. But all the interaction points with DOM, jQuery, datatables, ajax etc makes it really complicated to refactor things in the way we do in Java world to make it more testable.

So, any suggestions for the above to sample cases would be greatly appreciated!

like image 999
Ashkan Aryan Avatar asked Jun 18 '12 11:06

Ashkan Aryan


2 Answers

To test the following code:

function enableOccupationDetailsText (){
    $( "#fldOccupation" ).val("Unknown");
    $( "#fldOccupation_Details" ).attr("disabled", "");
    $( "#fldOccupation_Details" ).val("");
    $( "#fldOccupation_Details" ).focus();
}

You could use the following code:

// First, create the elements
$( '<input>', { id: 'fldOccupation' } ).appendTo( document.body );
$( '<input>', { disabled: true, id: 'fldOccupation_Details' } )
    .appendTo( document.body );

// Then, run the function to test
enableOccupationDetailsText();

// And test
assert( $( '#fldOccupation' ).val(), 'Unknown' );
assert( $( '#fldOccupation_Details' ).prop( 'disabled' ), false );

As you see, it's just the classical setup > run > assert pattern.

like image 66
Florian Margaine Avatar answered Nov 14 '22 23:11

Florian Margaine


maybe Selenium/SeleniumGrid is usefull for you: http://seleniumhq.org/

It is not a "UnitTest" per definition, but you can write selenium tests with java or python (and many more...) as unit tests. Seleniumtests starts web tests in real browsers, and is quiet good for testing frontends (and dom manipulations).

Edit: today i stumbled upon this site describing different ways for unit-testing especially in a jQuery background: http://addyosmani.com/blog/jquery-testing-tools/

like image 45
itinance Avatar answered Nov 15 '22 00:11

itinance