【问题标题】:How to unit test Javascript code that interacts with DOM elements如何对与 DOM 元素交互的 Javascript 代码进行单元测试
【发布时间】:2012-06-20 08:34:57
【问题描述】:

背景:

我来自 Java 背景,所以对 Javascript 不太熟悉。

我们计划在现有(遗留)代码和未来工作中引入 JavaScript 单元测试。我们主要是一家java商店(Spring、Weblogic等)。

我们正在寻找能够与 IDE(IntelliJ 理念)和声纳良好集成的选项,以及能够将它们作为持续集成的一部分运行。

JsTestDriver 似乎勾选了所有选项。

问题:

我们现有的许多 javascript 代码 a) 嵌入到 JSP 中,并且 b) 利用 jQuery 直接与页面元素交互。

我们应该如何测试一个严重依赖 DOM 的函数。这是我正在谈论的函数的一些代码示例:

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

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();

}
);

在后一种情况下,我的方法是,所讨论的函数太大了,应该分解成更小的(单位),以便对其进行测试。但是与 DOM、jQuery、datatables、ajax 等的所有交互点使得像我们在 Java 世界中所做的那样重构事物以使其更具可测试性变得非常复杂。

因此,对于上述示例案例的任何建议将不胜感激!

【问题讨论】:

标签: javascript jquery unit-testing qunit js-test-driver


【解决方案1】:

也许 Selenium/SeleniumGrid 对你有用:http://seleniumhq.org/

根据定义,它不是“UnitTest”,但您可以使用 java 或 python(以及更多...)编写 selenium 测试作为单元测试。 Seleniumtests 在真实浏览器中启动 Web 测试,非常适合测试前端(和 dom 操作)。

编辑:今天我偶然发现这个网站描述了单元测试的不同方法,尤其是在 jQuery 背景中:http://addyosmani.com/blog/jquery-testing-tools/

【讨论】:

    【解决方案2】:

    测试以下代码:

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

    您可以使用以下代码:

    // 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 );
    

    如您所见,这只是经典的setup &gt; run &gt; assert 模式。

    【讨论】:

    • 如何拆除那些 DOM 对象,这样您就不会让一个测试干扰另一个测试?那会是一个好习惯吗?如果有,怎么做?
    • @GregWoods 很好,您可以使用$('#fldOccupation').remove(); $('#fldOccupation_Details').remove(); 或简单的$( document.body.children ).remove(); 之类的方式删除它们
    猜你喜欢
    • 2020-06-22
    • 1970-01-01
    • 2023-03-17
    • 2013-12-12
    • 1970-01-01
    • 1970-01-01
    • 2010-09-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多