10

在许多框架中,内部函数变量被用作私有变量,例如

Raphael = (function(){
    var _private = function(a,b) {return a+b;};
    var _public = function(a) {return _private(a,a);}
    var object = {mult2:_public};
    return object;
})();

在这里,我们无法从全局命名空间访问名为 的变量private,因为它是第一行中匿名函数的内部变量。

有时这个函数包含一个大的 Javascript 框架,所以它不会污染全局命名空间。

我需要对Raphael内部使用的某些对象进行单元测试(在上面的示例中,我希望对对象运行单元测试private)。我该如何测试它们?

编辑:我收到了关于应该测试公共接口的单元测试的评论。

让我指定一个用例。我正在编写一个名为Raphael. 这个库应该只向全局命名空间添加一个名称,仅此而已。这是 Javascript 的特殊要求,因为 Javascript 没有命名空间。

假设Raphael使用链表。如果 Javascript 有包的概念,我会做

require 'linked_list'
Raphael = (function(){/* use linked list */})();

但是,Javascript 不允许我以任何不会污染链表对象的全局范围的方式来执行此操作!因此,我必须内联linked_list到 Raphael 的本地范围:

Raphael = (function(){
    /* implement linked list */
    var linked_list = function(){/*implementation*/};
})();

现在我想测试linked_list实现。

4

4 回答 4

12

你仍然没有抓住重点。

单元测试的重点是验证对象的公共接口是否符合预期。单元测试显示代码是如何工作的。

唯一需要测试的是对象的公共接口。这样,当您的开发人员想要更改对象的实现方式时,您只需要担心被测试的对象是否仍然按照预期执行。

如果您觉得该闭包内的对象需要测试,则对其进行测试,但在外部进行,然后将其传递给闭包。

var Raphael= function(listIterator) {
  listIterator.method();
}(new ListIterator());

如下所示的虚假 hack 是完全不合适的(在单元测试或任何地方)。

测试函数应该是简单的,只测试一件事,并且有一个断言。这通常发生在三到十行测试代码中。

当您的测试功能变得复杂时,因为它们将遵循您所询问的方法,然后(1)意识到您的设计可能不是您想要的,并对其进行更改以使其成为, 或 (2) 改变你对测试的期望。

关于您发布的代码,您忘记了var,错过了一个分号,并使用了两个保留字作为标识符:privatepublic

不使用的后果var是可能触发与非标准类型对象的各种实现相关的错误和问题GlobalScopePolluter(在 IE 中看到“对象不支持此属性或方法”)。使用 FutureReservedWord 的结果是SyntaxError. 实现可能会提供语法扩展以允许FutureReservedWord 作为标识符,并且确实有很多这样做,但是最好不要依赖此类扩展,如果您遇到错误,那将完全是您的错。

您提到向用户交付代码。我建议你在获得更多经验和理解你正在做的事情之前不要这样做。

// DO NOT USE THIS CODE.
var Raphael = (function(){
    var _private = function(a,b) {return a+b;};
    var _public = function(a) {return _private(a,a);};
    var object = {mult2:_public};
    return object;
})();

var leakedFunction;

// Spurious hack:
//   Give valueOf a side effect of leaking function.
//   valueOf is called by the _private function as a
//   side effect of primitive conversion, where 
//   ToPrimitive(input argument, hint Number) results 
//   in calling valueOf.

function valueOfSnoop(){ 
    leakedFunction = leakedFunction || valueOfSnoop.caller || function(){};
    return 2;
}

var a = {
  valueOf : valueOfSnoop
};

Raphael.mult2(a, 3);
var privateMathod = leakedFunction;
alert(leakedFunction(1, 2));

该示例代码只是为了证明这种事情是可能的。鉴于选择,它是前面提到的替代方案的糟糕替代方案;要么改变你的设计,要么改变你的测试。

于 2010-05-05T21:28:56.563 回答
2

尝试这个:

var adder = function(a,b) {
    return a + b;
}

Raphael = function(fn){
    var _private = function(a,b) {
        fn(a,b);
    }

    var _public = function(a) {
        return _private(a,a);
    }

    var object = {doubleIt: _public};

    return object;
}(adder);

只是一点函数注入

于 2010-05-05T20:03:21.680 回答
1

我想出的最佳解决方案:

在源 Javascript 文件中使用

Raphael = (function(){
// start linked_list
    var linked_list = function() {/*...*/};
// end linked_list
    var object = {mult2:_public};
    return object;
})();

现在,使用脚本提取 和 之间的对象// start ([a-zA-Z_]*)// end ([a-zA-Z_]*)并对提取的代码进行单元测试。

显然,从外部范围访问函数内部范围内的变量是不可能的。正如在评论中链接到的 SO 问题 Jason 中所写。

于 2010-05-06T04:31:57.217 回答
0
var Raphael;
var test = true; //or false;

Raphael = (function(){
    var private = function(a,b) {return a+b;};
    var public = function(a) {return private(a,a);}
    var object = {mult2:public};

    if (test) Raphael.private = private;

    return object;
})();
于 2010-05-05T18:36:59.913 回答