在许多框架中,内部函数变量用作私有变量,例如
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
实现。
答案 0 :(得分:12)
你仍然忽略了这一点。
单元测试的目的是验证对象的公共接口是否符合预期。单元测试显示代码的工作原理。
唯一需要测试的是对象的公共接口。这样,当开发人员想要更改对象的实现方式时,您只会担心被测对象是否仍然能够达到预期的效果。
如果你觉得那个闭包内的对象需要测试,那么测试一下,但是在外部做,然后将它传递给闭包。
var Raphael= function(listIterator) {
listIterator.method();
}(new ListIterator());
虚假黑客,如下所示,完全不合适(在单元测试或任何地方)。
测试函数应该简单,只测试一件事,并有一个断言。这通常可以在三到十行测试代码中发生。
当你到达测试功能复杂的程度时,因为他们会遵循你所询问的方法,然后要么 (1)意识到你的设计可能不是你想要的,并改变它,或者(2)改变你对测试的期望。
关于您发布的代码,您忘记了var
,错过了分号,并使用了两个保留字作为标识符:private
和public
。
不使用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));
该示例代码仅用于证明这样的事情是可能的。鉴于这种选择,它是前面提到的替代品的不良替代品;要么改变你的设计,要么改变你的测试。
答案 1 :(得分: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);
只需一点功能注射
答案 2 :(得分: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_]*)
之间提取对象,并对提取的代码进行单元测试。
显然,无法从外部作用域访问函数内部范围中的变量。正如Jason在评论中所提到的SO问题所写的那样。
答案 3 :(得分: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;
})();