如何在jQuery插件中对私有方法进行单元测试?

13

也许这是一个初学者 JQuery 的问题:

  • 正确的 JQuery 插件是在闭包内编写的
  • 因此只有定义插件接口的方法可从外部访问
  • 有时候(或经常)需要辅助方法,但不应将其作为插件接口的一部分公开(例如,因为它们会改变内部状态)。
  • 那么这些辅助方法该如何进行单元测试呢?

例如,查看 blockUI 插件,如何对安装、删除和重置方法进行单元测试?

为了类比,如果是 Java:

  1. 创建一个仅含公共方法的 BlockUI 接口(根据定义)
  2. 创建实现上述接口的 BlockUIImpl 类。该类将包含 install()、remove() 和 reset() 方法,这些方法可以是公共的或(包)受保护的

因此,我会对 Impl 进行单元测试,客户端程序员将通过 BlockUI 接口与插件进行交互。


这里有一个适用于类似问题的答案:exposure by injection,并提供了一个使用qUnit和jQuery的示例here - Sjeiti
3个回答

13

与其他语言和测试私有方法一样,此处同样适用:要测试私有方法,您应该通过公共接口调用它们。换句话说,通过调用您的公共方法,私有方法会在过程中得到测试,因为公共方法依赖于私有方法。

通常情况下,私有方法不会单独从公共接口进行测试 - 整个重点是它们是实现细节,测试通常不应该过多地了解实现细节。


好的,关于私有方法本身是可以的。但是,有没有办法模仿我提到的接口/实现类方法? - Nikita
1
根据我的经验,在JS中试图过度模仿Java或类似语言通常只会让事情变得更加复杂,没有任何好处... 如果你想将某些方法标记为私有的,通常可以使用JSDoc注释或在名称前加下划线来实现。 - Jani Hartikainen
正确 - 那么作者如何在不暴露给插件用户的情况下测试其正确性呢?它被从两个公共方法中调用,这些方法做了很多其他的事情,因此在单元测试中调用任一方法都不能很好地验证“remove”是否按设计工作。应该有一种方法可以隔离地测试remove(),同时仍然不将其暴露给插件用户,对吧? - Nikita
1
整个重点在于您不需要测试私有方法。如果公共方法正常工作,则可以假设私有方法也正常工作。如果私有方法存在错误,则公共方法也无法正常工作。 - Jani Hartikainen
1
@JaniHartikainen 这很有道理。而且,编写测试非常无聊。我越少写,但仍然保持良好的良心,就越好。 - Camilo Martin
显示剩余2条评论

2

在JavaScript中,函数内部编写的代码,或者你所说的闭包,不一定与该函数外部隔离。

了解函数所定义的范围对于其可见性很有用。您创建的任何闭包都携带着包含它的代码的作用域和函数。

以下是一个使用jQuery插件和人工“命名空间”的简单示例,可以证明这个假设:

// Initialise this only when running tests
my_public_test_namespace = function(){};

jQuery.fn.makeItBlue = function() {

    makeItBlue(this);

    function makeItBlue(object) {
        object.css('color','blue');
    }

    if(typeof my_public_test_namespace != "undefined") {
        my_public_test_namespace.testHarness = function() {
            return {
                _makeItBluePrivateFn: makeItBlue
            }
        };
    }
};

$("#myElement").makeItBlue(); // make something blue, initialise plugin

console.debug(my_public_test_namespace.testHarness()._makeItBluePrivateFn);

但不要忘记,您实际上不应该测试私有内容。 ;)


2
你能解释一下为什么不应该测试私有函数吗?我认为对于复杂的代码和/或回归测试来说,这可能非常有用。 - Sjeiti
嗨,Sjeiti。这在Jani的答案中已经解释了。也许你想评论那个答案。干杯。 - Zero Distraction

0

我也遇到了同样的问题,在浏览并找到不太适用的答案后,我最终解决了类似的问题。

问题:“我有一个小部件,它具有我想要测试以确保其按预期工作的行为,一些方法是内部调用的,因为它们必须解决内部行为,将它们公开为公共方法没有意义,因为它们不会从外部调用,测试公共方法意味着您不会测试小部件的内部,那么最后我该怎么办?”

解决方案:“创建一个测试小部件,公开您感兴趣的测试方法,并在qunit中使用它们,这是一个示例:”

// Namespaces to avoid having conflicts with other things defined similarly
var formeditortest = formeditortest || {};
// widget that inherits from the container I want to test
$.widget( "app.testcontainer", $.app.container,  {
    executeDrop: function(drop, helper) {
       var self = this;
       self._executeDrop(drop, helper);
    }
});
// Test cases
formeditortest.testDropSimple = function(assert) {
   var container = $("<div />");
   container.testcontainer();
   container.testcontainer("drop", 0, 3);
   assert.equal(true, $(innerDiv.children()[0]).hasClass("droparea"));
});

QUnit.test(name, function( assert ) {
   formeditortest.testDropSimple(assert);
   formeditortest.testDropBottom(assert);
});

使用这种方法,继承的测试容器可以准备好测试元素,然后qunit将处理测试,这解决了我的问题,希望这对于其他遇到类似测试问题的人也有所帮助。
欢迎评论批评,如果我做错了什么,我想改进它!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接