Javascript 模块模式单元测试

Javascript 模块模式单元测试,javascript,jquery,unit-testing,module,Javascript,Jquery,Unit Testing,Module,在以下假设导航模块的实现中,模块对象返回属性,如isoverbined或isNavTurnedOff,这些属性基本上返回其他方法的相应值 然后在测试用例中使用这些方法来检查属性调用是否导致了预期的结果 这些方法是否应该保留,或者问题中的原始方法是否返回相应的值以及测试用例中使用的相同方法 目前的代码是: var navModule = (function(element) { var nav = {}; var navHTMLobjs = {

在以下假设导航模块的实现中,模块对象返回属性,如
isoverbined
isNavTurnedOff
,这些属性基本上返回其他方法的相应值

然后在测试用例中使用这些方法来检查属性调用是否导致了预期的结果

这些方法是否应该保留,或者问题中的原始方法是否返回相应的值以及测试用例中使用的相同方法

目前的代码是:

var navModule = (function(element) {

        var nav = {};

        var navHTMLobjs = {

            navList : element,

            listItems : element.find('li'),

            listLinks : element.find('a')

        };

        nav.bindOver = function() {

            navHTMLobjs.navList.on('mouseover mouseout', 'li a', function(e) {

                if (e.type == 'mouseover') {

                    $(this).addClass('over');

                }

                if (e.type == 'mouseout') {

                    $(this).removeClass('over');
                }

            });

        };

        nav.isOverBinded = function(){

            return navHTMLobjs.navList.data('events').hasOwnProperty('mouseover')

                && navHTMLobjs.navList.data('events').hasOwnProperty('mouseout');

        };

        nav.turnOff = function() {

            navHTMLobjs.navList.off('mouseover mouseout');

        };

        nav.isNavTurnedOff = function() {

            return !navHTMLobjs.navList.data.hasOwnProperty('events');

        };

        nav.init = function() {

            this.bindOver();

        };

        return nav;

    });

    var myNav = new navModule($('#nav'));

    /// Test cases:

    module('Navigation module');

    test('Binding total', function() {

        myNav.init();

        equal(myNav.isOverBinded(), true, "Does the init function attach all events?");

    });

    test('Unbinding total', function() {

        myNav.turnOff();

        equal(myNav.isNavTurnedOff(), true, "Does the cancel function correctly unbind events?");

    });
例如,我应该将
nav.bingOver
更改为:

nav.bindOver = function() {

            navHTMLobjs.navList.on('mouseover mouseout', 'li a', function(e) {

                if (e.type == 'mouseover') {

                    $(this).addClass('over');

                }

                if (e.type == 'mouseout') {

                    $(this).removeClass('over');
                }

            });

            return navHTMLobjs.navList.data('events').hasOwnProperty('mouseover')

                && navHTMLobjs.navList.data('events').hasOwnProperty('mouseout');

        };
…然后在下面的测试用例中使用相同的方法

test('Binding total', function() {

        myNav.init();

        equal(myNav.bindOver(), true, "Does the init function attach all events?");

    });
两者之间有什么区别


非常感谢

假设应用程序的其他部分不需要独立验证事件是否已订阅,
bindOver()
不应返回任何值。此外,
isoverbined()
不属于导航模块。它的存在纯粹是为了帮助实现测试。在这种情况下,该功能应该在测试套件中

var navModule = (function(element) {

    var nav = {};

    var navHTMLobjs = {
        navList : element,
        listItems : element.find('li'),
        listLinks : element.find('a')
    };

    nav.bindOver = function() {
        navHTMLobjs.navList.on('mouseover mouseout', 'li a', function(e) {
            if (e.type == 'mouseover') {
                $(this).addClass('over');
            }

            if (e.type == 'mouseout') {
                $(this).removeClass('over');
            }
        });
    };

    nav.turnOff = function() {
        navHTMLobjs.navList.off('mouseover mouseout');
    };

    nav.init = function() {
        this.bindOver();
    };

    return nav;
});

//var myNav = new navModule($('#nav'));

/// Test cases:

module('Navigation module');

// you might already have such a in memory object 
$root = $('<ul></ul>').append('<li><a href="#"></a></li><li><a href="#"></a></li>'); 
var myNav = new navModule($root);

test('Binding total', function() {

    myNav.init();

    equal(isOverBinded(), true, "Does the init function attach all events?");

});

test('Unbinding total', function() {

    myNav.turnOff();

    equal(isNavTurnedOff(), true, "Does the cancel function correctly unbind events?");

});

var isNavTurnedOff = function() {
    return $root.data('events').hasOwnProperty('mouseover') && $root.data('events').hasOwnProperty('mouseout');
}

var isOverBinded = function() {
    return $root.data.hasOwnProperty('events') === false;
}
var navModule=(函数(元素){
var nav={};
var navHTMLobjs={
导航列表:元素,
listItems:element.find('li'),
listLinks:element.find('a')
};
nav.bindOver=函数(){
on('mouseover mouseout','li a',function(e){
如果(e.type=='mouseover'){
$(this.addClass('over');
}
如果(e.type=='mouseout'){
$(this.removeClass('over');
}
});
};
导航关闭=功能(){
navHTMLobjs.navList.off('mouseover mouseout');
};
nav.init=函数(){
这是bindOver();
};
返回导航;
});
//var myNav=新的导航模块($('#nav');
///测试用例:
模块(“导航模块”);
//您可能已经有了这样一个内存中对象
$root=$('
    ')。追加('
  • '); var myNav=新的导航模块($root); 测试('绑定总计',函数(){ myNav.init(); equal(isOverBinded(),true,“init函数是否附加所有事件?”); }); 测试('Unbinding total',函数(){ 我的导航关闭(); 相等(isNavTurnedOff(),true,“取消函数是否正确解除事件绑定?”); }); var isNavTurnedOff=函数(){ 返回$root.data('events').hasOwnProperty('mouseover')和&$root.data('events').hasOwnProperty('mouseout'); } var isOverBinded=函数(){ 返回$root.data.hasOwnProperty('events')==false; }

    最后,我觉得函数是否应该返回值应该取决于函数的使用情况,而不是为了使测试更容易

    这在很大程度上是否取决于您打算如何使用函数?函数返回一个合适的值是有好处的。它使您在测试时不必管理状态。同时,如果你必须独立检查活动是否已订阅,因为应用程序的某些其他部分依赖于该信息,你可能希望将其作为一个函数公开。我写这篇文章不是为了回答,因为我觉得没有正确的答案。也许有更多经验的人会有一个。谢谢你对这个话题的关注。我想我脑子里的问题来自于返回值的偶尔复杂性,这是在模块运行时运行的额外代码,所以即使我知道函数总是返回一些东西是好的,我也不愿意在每次节运行时都有这么长的行作为返回值,因此,将其作为单独的方法进行分离。我想另一个问题是,虽然函数应该返回一些允许其可测试性的东西,但如果必须阅读这么长的行,那么应该在多大程度上遵循这一点?还是计算返回值需要时间?因为如果它是第一个函数,那么您可以选择
    返回isoverbined()
    ,其中
    isoverbined
    是本地私有函数,而不是公共函数。就我个人而言,当我有稍微复杂或长的谓词时,我就是这么做的。将实际计算隐藏在一个具有自我解释名称的函数后面。再仔细考虑一下,绑定方法实际上不需要返回任何东西。在实际的生产代码中,您不需要检查返回值。如何测试活动是否已订阅?视情况而定。您可以在测试时插入内存中的jquery对象,而不是使用html页面中的dom元素。实际上,您需要的属性只存储在内存中,而不是存储在实际的html中。这样,在您的测试中,您可以检查注入对象中的副作用,并且您的实际模块不必关心该检查。如果您可以在回答部分用一些代码扩展您的最后一条注释,那将是一件好事。我想这将是这个问题的正确答案。好的,阿弥斯。我从中得到了更多启示:)谢谢