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(上記の例では、 object に対して単体テストを実行したいと考えていますprivate)。どうすればそれらをテストできますか?

編集:パブリック インターフェイスをテストすることになっている単体テストについてコメントを受け取りました。

ユースケースを指定しましょう。というライブラリを書いていますRaphael。このライブラリは、単一の名前のみをグローバル名前空間に追加することになっています。Javascript には名前空間がないため、これは Javascript に固有の要件です。

Raphaelリンクされたリストを使用するとしましょう。Javascriptにパッケージの概念があれば、私はそうするでしょう

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

ただし、Javascript では、リンクされたリスト オブジェクトでグローバル スコープを汚染しないような方法でそれを行うことはできません! したがって、linked_listRaphael のローカル スコープにインライン化する必要があります。

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

linked_listそして今、実装をテストしたいと思います。

4

4 に答える 4

12

あなたはまだ要点を見逃しています。

単体テストのポイントは、オブジェクトのパブリック インターフェイスが期待どおりに動作することを確認することです。単体テストは、コードがどのように機能するかを示します。

テストする必要があるのは、オブジェクトのパブリック インターフェイスだけです。そうすれば、開発者がオブジェクトの実装方法を変更したい場合、テスト対象のオブジェクトが期待どおりの動作をするかどうかだけを気にするだけです。

そのクロージャー内にあるオブジェクトをテストする必要があると思われる場合は、それをテストしますが、外部でテストしてからクロージャーに渡します。

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

以下に示すような偽のハッキングは、まったく不適切です (単体テストでもどこでも)。

テスト関数は単純で、1 つのことだけをテストし、1 つのアサーションを持つ必要があります。これは通常、3 ~ 10 行のテスト コードで発生する可能性があります。

テスト機能が複雑になると、質問しているアプローチに従っているため、(1) 設計が望んでいたものではない可能性があることに気付き、設計が、または(2)テストでの期待を変更します。

あなたが投稿したコードに関して、あなたは を忘れvar、セミコロンを見逃し、2 つの予約語を識別子として使用していました: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 の質問ジェイソンに書かれているように。

于 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 に答える