在Javascript中访问内部函数变量

Ela*_*ich 9 javascript unit-testing private-methods

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

Raphael = (function(){
    var _private = function(a,b) {return a+b;};
    var _public = function(a) {return _private(a,a);}
    var object = {mult2:_public};
    return object;
})();
Run Code Online (Sandbox Code Playgroud)

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

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

我需要在Raphael内部单元测试一些对象用法(在上面的例子中,我希望对对象运行单元测试private).我该如何测试它们?

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

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

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

require 'linked_list'
Raphael = (function(){/* use linked list */})();
Run Code Online (Sandbox Code Playgroud)

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

Raphael = (function(){
    /* implement linked list */
    var linked_list = function(){/*implementation*/};
})();
Run Code Online (Sandbox Code Playgroud)

现在我想测试linked_list实现.

Gar*_*ett 12

你仍然忽略了这一点.

单元测试的要点是验证对象的公共接口是否符合预期.单元测试显示代码的工作原理.

唯一应该测试的是对象的公共接口.这样,当开发人员想要更改对象的实现方式时,您只会担心被测对象是否仍然能够实现预期的对象.

如果你觉得那个闭包里面的对象需要测试,那么测试一下,但是在外部做,然后把它传递给闭包.

var Raphael= function(listIterator) {
  listIterator.method();
}(new ListIterator());
Run Code Online (Sandbox Code Playgroud)

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

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

当你到达测试函数复杂的程度时,因为他们将遵循你所询问的方法,然后(1)意识到你的设计可能不是你想要的那样并且改变它以便它是,或(2)改变您对测试的期望.

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

不使用的结果var是可能触发与非标准GlobalScopePolluter类型对象的各种实现相关的错误和问题("IE不支持此属性或方法"在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));
Run Code Online (Sandbox Code Playgroud)

该示例代码仅用于证明这样的事情是可能的.鉴于这种选择,它是前面提到的替代品的不良替代品; 要么改变你的设计,要么改变你的测试.


Jam*_*ate 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;
})();
Run Code Online (Sandbox Code Playgroud)